From: Pekka Riikonen Date: Fri, 16 Feb 2001 23:28:53 +0000 (+0000) Subject: updates. X-Git-Tag: SILC.0.1~215 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=10abd339ccd4ef4b5540d2bee269c8edba4fd9e7 updates. --- diff --git a/CHANGES b/CHANGES index 313cda5e..2d24b196 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,67 @@ +Sat Feb 17 01:06:44 EET 2001 Pekka Riikonen + + * Added new function into the silcd/server.[ch] files: + silc_server_create_new_channel_with_id to create new channel with + already existing Channel ID. + + * Added new packet type SILC_PACKET_SET_MODE_LIST into the file + lib/silccore/silcpacket.h. This packet is used t send list of + Set Mode payloads inside one packet. Server uses this to set + the modes for the channels and clients on those channels, that it + announced to the router when it connected to it. The protocol + specification has been updated accordingly. + + * The silc_server_new_channel did not handle the packet coming + from normal server as it normally does not send that. However, + when it announces its channels it does send it. Implemented + the support for that. + + * Added SILC_ID_CHANNEL_COMPARE macro to compare to Channel ID's + into the file lib/silccore/id.h. + +Fri Feb 16 23:57:29 EET 2001 Pekka Riikonen + + * Fixed memory leaks in the functions silc_idlist_del_client, + silc_idlist_del_channel and silc_idlist_del_server in the file + silcd/idlist.c. All of those leaked like a sieve. + + * Fixed some small memory leaks in the client's function + silc_client_notify_by_server. + + * Added functions into silcd/server.c: silc_server_announce_clients, + silc_server_announce_channels and silc_server_announce_server. + These functions are used by normal and router server to announce + to its primary router about clients, channels and servers (when + router) that we own. This is done after we've connected to the + router. + + These functions effectively implements the following packet types: + SILC_PACKET_NEW_CHANNEL_LIST, SILC_PACKET_NEW_CHANNEL_USER_LIST + and SILC_PACKET_NEW_ID_LIST. + + * Added new functions into the silcd/packet_receive.[ch]: + silc_server_new_id_list, silc_server_new_channel_list and + silc_server_new_channel_user_list to handle the incoming + NEW_ID_LIST, NEW_CHANNEL_LIST and NEW_CHANNEL_USER_LIST packets. + + * Added support of changing Channel ID in the function + silc_server_replace_id. If the server that announces a channel + to the router already exists in the router (with same name but + with different Channel ID), router is responsible to send + Replace ID packet to the server and force the server to change + the Channel ID to the one router has. + + * Added new notify type SILC_NOTIFY_TYPE_CHANNEL_CHANGE to notify + client that the Channel ID has been changed by the router. The + normal server sends this to the client. Client must start using + the new Channel ID as the channel's ID. + + Implemented handling of this new type into lib/silcclient/client.c + into the function silc_client_notify_by_server. + + * Added new function silc_idlist_replace_channel_id into the files + silcd/idlist.[ch] to replace the Channel ID. + Fri Feb 16 14:14:00 EET 2001 Pekka Riikonen * Call silc_server_command_identify_check always when processing diff --git a/apps/silc/client_ops.c b/apps/silc/client_ops.c index 4097e9f8..cb730293 100644 --- a/apps/silc/client_ops.c +++ b/apps/silc/client_ops.c @@ -219,6 +219,9 @@ void silc_notify(SilcClient client, SilcClientConnection conn, } return; + case SILC_NOTIFY_TYPE_CHANNEL_CHANGE: + break; + default: break; } diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index 1d6b9540..6323af36 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -172,6 +172,9 @@ void silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry) silc_free(entry->server_name); if (entry->id) silc_free(entry->id); + + memset(entry, 'F', sizeof(*entry)); + silc_free(entry); } } @@ -238,6 +241,9 @@ void silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry) silc_free(entry->userinfo); if (entry->id) silc_free(entry->id); + + memset(entry, 'F', sizeof(*entry)); + silc_free(entry); } } @@ -528,16 +534,19 @@ int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry) memset(entry->key, 0, entry->key_len / 8); silc_free(entry->key); } - memset(entry->iv, 0, sizeof(entry->iv)); silc_list_start(entry->user_list); while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) { silc_list_del(entry->user_list, chl); silc_free(chl); } + + memset(entry, 'F', sizeof(*entry)); + silc_free(entry); + return TRUE; } - return TRUE; + return FALSE; } /* Finds channel by channel name. Channel names are unique and they @@ -601,3 +610,30 @@ silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id, return channel; } + +/* Replaces old Channel ID with new one. This is done when router forces + normal server to change Channel ID. */ + +SilcChannelEntry +silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id, + SilcChannelID *new_id) +{ + SilcIDCacheEntry id_cache = NULL; + SilcChannelEntry channel; + + if (!old_id || !new_id) + return NULL; + + SILC_LOG_DEBUG(("Replacing Channel ID")); + + if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, + SILC_ID_CHANNEL, &id_cache)) + return NULL; + + channel = (SilcChannelEntry)id_cache->context; + silc_free(channel->id); + channel->id = new_id; + id_cache->id = (void *)new_id; + + return channel; +} diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index cbdf64f8..4940b6c8 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -503,5 +503,8 @@ silc_idlist_find_channel_by_name(SilcIDList id_list, char *name, SilcChannelEntry silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id, SilcIDCacheEntry *ret_entry); +SilcChannelEntry +silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id, + SilcChannelID *new_id); #endif diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index c3978f70..9321add3 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -385,18 +385,21 @@ void silc_server_replace_id(SilcServer server, case SILC_ID_SERVER: SILC_LOG_DEBUG(("Old Server ID id(%s)", - silc_id_render(id, SILC_ID_CLIENT))); + silc_id_render(id, SILC_ID_SERVER))); SILC_LOG_DEBUG(("New Server ID id(%s)", - silc_id_render(id2, SILC_ID_CLIENT))); + silc_id_render(id2, SILC_ID_SERVER))); if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL) if (server->server_type == SILC_ROUTER) silc_idlist_replace_server_id(server->global_list, id, id2); break; case SILC_ID_CHANNEL: - /* XXX Hmm... Basically this cannot occur. Channel ID's cannot be - re-generated. */ - silc_free(id2); + SILC_LOG_DEBUG(("Old Channel ID id(%s)", + silc_id_render(id, SILC_ID_CHANNEL))); + SILC_LOG_DEBUG(("New Channel ID id(%s)", + silc_id_render(id2, SILC_ID_CHANNEL))); + if (silc_idlist_replace_channel_id(server->local_list, id, id2) == NULL) + silc_idlist_replace_channel_id(server->global_list, id, id2); break; default: @@ -777,6 +780,245 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock, silc_id_payload_free(idp); } +/* Receoved New Id List packet, list of New ID payloads inside one + packet. Process the New ID payloads one by one. */ + +void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcPacketContext *new_id; + SilcBuffer idp; + unsigned short id_len; + + SILC_LOG_DEBUG(("Processing New ID List")); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER) + return; + + /* Make copy of the original packet context, except for the actual + data buffer, which we will here now fetch from the original buffer. */ + new_id = silc_packet_context_alloc(); + new_id->type = SILC_PACKET_NEW_ID; + new_id->flags = packet->flags; + new_id->src_id = packet->src_id; + new_id->src_id_len = packet->src_id_len; + new_id->src_id_type = packet->src_id_type; + new_id->dst_id = packet->dst_id; + new_id->dst_id_len = packet->dst_id_len; + new_id->dst_id_type = packet->dst_id_type; + + idp = silc_buffer_alloc(256); + new_id->buffer = idp; + + while (packet->buffer->len) { + SILC_GET16_MSB(id_len, packet->buffer->data + 2); + if ((id_len > packet->buffer->len) || + (id_len > idp->truelen)) + break; + + silc_buffer_pull_tail(idp, 4 + id_len); + silc_buffer_put(idp, packet->buffer->data, 4 + id_len); + + /* Process the New ID */ + silc_server_new_id(server, sock, new_id); + + silc_buffer_push_tail(idp, 4 + id_len); + silc_buffer_pull(packet->buffer, 4 + id_len); + } + + silc_buffer_free(idp); + silc_free(new_id); +} + +/* Received New Channel packet. Information about new channels in the + network are distributed using this packet. Save the information about + the new channel. This usually comes from router but also normal server + can send this to notify channels it has when it connects to us. */ + +void silc_server_new_channel(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + unsigned char *id; + SilcChannelID *channel_id; + unsigned short channel_id_len; + char *channel_name; + int ret; + + SILC_LOG_DEBUG(("Processing New Channel")); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER || + server->server_type == SILC_SERVER) + return; + + /* Parse payload */ + ret = silc_buffer_unformat(packet->buffer, + SILC_STR_UI16_STRING_ALLOC(&channel_name), + SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len), + SILC_STR_END); + if (ret == -1) { + if (channel_name) + silc_free(channel_name); + if (id) + silc_free(id); + return; + } + + /* Decode the channel ID */ + channel_id = silc_id_str2id(id, channel_id_len, SILC_ID_CHANNEL); + if (!channel_id) + return; + + if (sock->type == SILC_SOCKET_TYPE_ROUTER) { + /* Add the server to global list as it is coming from router. It + cannot be our own channel as it is coming from router. */ + + SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s", + silc_id_render(channel_id, SILC_ID_CHANNEL), + sock->hostname)); + + silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, + server->router->connection, NULL); + + server->stat.channels++; + } else { + /* The channel is coming from our server, thus it is in our cell + we will add it to our local list. */ + SilcChannelEntry channel; + SilcBuffer chk; + + SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s", + silc_id_render(channel_id, SILC_ID_CHANNEL), + sock->hostname)); + + /* Check that we don't already have this channel */ + channel = silc_idlist_find_channel_by_name(server->local_list, + channel_name, NULL); + if (!channel) + channel = silc_idlist_find_channel_by_name(server->global_list, + channel_name, NULL); + + /* If the channel does not exist, then create it. We create the channel + with the channel ID provided by the server. This creates a new + key to the channel as well that we will send to the server. */ + if (!channel) { + channel = silc_server_create_new_channel_with_id(server, NULL, + channel_name, + channel_id); + if (!channel) + return; + + /* Send the new channel key to the server */ + chk = silc_channel_key_payload_encode(channel_id_len, id, + strlen(channel->channel_key-> + cipher->name), + channel->channel_key->cipher->name, + channel->key_len / 8, + channel->key); + silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, + chk->data, chk->len, FALSE); + silc_buffer_free(chk); + + } else { + /* The channel exist by that name, check whether the ID's match. + If they don't then we'll force the server to use the ID we have. + We also create a new key for the channel. */ + + if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) { + /* They don't match, send Replace ID packet to the server to + force the ID change. */ + SILC_LOG_DEBUG(("Forcing the server to change Channel ID")); + silc_server_send_replace_id(server, sock, FALSE, + channel_id, SILC_ID_CHANNEL, + SILC_ID_CHANNEL_LEN, + channel->id, SILC_ID_CHANNEL, + SILC_ID_CHANNEL_LEN); + } + + /* Create new key for the channel and send it to the server and + everybody else possibly on the channel. */ + + silc_server_create_channel_key(server, channel, 0); + + /* Send to the channel */ + silc_server_send_channel_key(server, sock, channel, FALSE); + + /* Send to the server */ + chk = silc_channel_key_payload_encode(channel_id_len, id, + strlen(channel->channel_key-> + cipher->name), + channel->channel_key->cipher->name, + channel->key_len / 8, + channel->key); + silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, + chk->data, chk->len, FALSE); + silc_buffer_free(chk); + } + } + + silc_free(id); +} + +/* Received New Channel List packet, list of New Channel List payloads inside + one packet. Process the New Channel payloads one by one. */ + +void silc_server_new_channel_list(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcPacketContext *new; + SilcBuffer buffer; + unsigned short len1, len2; + + SILC_LOG_DEBUG(("Processing New Channel List")); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER || + server->server_type == SILC_SERVER) + return; + + /* Make copy of the original packet context, except for the actual + data buffer, which we will here now fetch from the original buffer. */ + new = silc_packet_context_alloc(); + new->type = SILC_PACKET_NEW_CHANNEL; + new->flags = packet->flags; + new->src_id = packet->src_id; + new->src_id_len = packet->src_id_len; + new->src_id_type = packet->src_id_type; + new->dst_id = packet->dst_id; + new->dst_id_len = packet->dst_id_len; + new->dst_id_type = packet->dst_id_type; + + buffer = silc_buffer_alloc(512); + new->buffer = buffer; + + while (packet->buffer->len) { + SILC_GET16_MSB(len1, packet->buffer->data); + if ((len1 > packet->buffer->len) || + (len1 > buffer->truelen)) + break; + + SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1); + if ((len2 > packet->buffer->len) || + (len2 > buffer->truelen)) + break; + + silc_buffer_pull_tail(buffer, 4 + len1 + len2); + silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2); + + /* Process the New Channel */ + silc_server_new_channel(server, sock, new); + + silc_buffer_push_tail(buffer, 4 + len1 + len2); + silc_buffer_pull(packet->buffer, 4 + len1 + len2); + } + + silc_buffer_free(buffer); + silc_free(new); +} + /* Received Remove Channel User packet to remove a user from a channel. Routers notify other routers that user has left a channel. Client must not send this packet. Normal server may send this packet but must not @@ -798,6 +1040,7 @@ void silc_server_remove_channel_user(SilcServer server, SILC_LOG_DEBUG(("Removing user from channel")); if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER || server->server_type == SILC_SERVER) return; @@ -859,57 +1102,62 @@ void silc_server_remove_channel_user(SilcServer server, silc_free(channel_id); } -/* Received New Channel packet. Information about new channels in the - network are distributed using this packet. Save the information about - the new channel. */ +/* Received New Channel User List packet, list of New Channel User payloads + inside one packet. Process the payloads one by one. */ -void silc_server_new_channel(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_server_new_channel_user_list(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { - unsigned char *id; - SilcChannelID *channel_id; - unsigned short channel_id_len; - char *channel_name; - int ret; + SilcPacketContext *new; + SilcBuffer buffer; + unsigned short len1, len2; - SILC_LOG_DEBUG(("Processing New Channel")); + SILC_LOG_DEBUG(("Processing New Channel User List")); - if (sock->type != SILC_SOCKET_TYPE_ROUTER || - server->server_type == SILC_SERVER || - packet->src_id_type != SILC_ID_SERVER) + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER || + server->server_type == SILC_SERVER) return; - /* Parse payload */ - ret = silc_buffer_unformat(packet->buffer, - SILC_STR_UI16_STRING_ALLOC(&channel_name), - SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len), - SILC_STR_END); - if (ret == -1) { - if (channel_name) - silc_free(channel_name); - if (id) - silc_free(id); - return; - } - - /* Decode the channel ID */ - channel_id = silc_id_str2id(id, channel_id_len, SILC_ID_CHANNEL); - if (!channel_id) - return; - silc_free(id); + /* Make copy of the original packet context, except for the actual + data buffer, which we will here now fetch from the original buffer. */ + new = silc_packet_context_alloc(); + new->type = SILC_PACKET_NEW_CHANNEL_USER; + new->flags = packet->flags; + new->src_id = packet->src_id; + new->src_id_len = packet->src_id_len; + new->src_id_type = packet->src_id_type; + new->dst_id = packet->dst_id; + new->dst_id_len = packet->dst_id_len; + new->dst_id_type = packet->dst_id_type; + + buffer = silc_buffer_alloc(256); + new->buffer = buffer; + + while (packet->buffer->len) { + SILC_GET16_MSB(len1, packet->buffer->data); + if ((len1 > packet->buffer->len) || + (len1 > buffer->truelen)) + break; - SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s", - silc_id_render(channel_id, SILC_ID_CHANNEL), - sock->hostname)); + SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1); + if ((len2 > packet->buffer->len) || + (len2 > buffer->truelen)) + break; + + silc_buffer_pull_tail(buffer, 4 + len1 + len2); + silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2); - /* Add the new channel. Add it always to global list since if we receive - this packet then it cannot be created by ourselves but some other - router hence global channel. */ - silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, - server->router->connection, NULL); + /* Process the New Channel User */ + silc_server_new_channel_user(server, sock, new); + + silc_buffer_push_tail(buffer, 4 + len1 + len2); + silc_buffer_pull(packet->buffer, 4 + len1 + len2); + } - server->stat.channels++; + silc_buffer_free(buffer); + silc_free(new); } /* Received notify packet. Server can receive notify packets from router. diff --git a/apps/silcd/packet_receive.h b/apps/silcd/packet_receive.h index 4b822a6d..2d0d3f3c 100644 --- a/apps/silcd/packet_receive.h +++ b/apps/silcd/packet_receive.h @@ -46,18 +46,26 @@ SilcServerEntry silc_server_new_server(SilcServer server, SilcPacketContext *packet); void silc_server_new_id(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); +void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock, + SilcPacketContext *packet); void silc_server_remove_channel_user(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); void silc_server_new_channel(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); -void silc_server_notify(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet); +void silc_server_new_channel_list(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); void silc_server_new_channel_user(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); +void silc_server_new_channel_user_list(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); +void silc_server_notify(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); void silc_server_remove_id(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index 83fef72f..05f0454e 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -1050,6 +1050,7 @@ void silc_server_send_new_channel(SilcServer server, SILC_STR_UI_XNSTRING(cid, channel_id_len), SILC_STR_END); + silc_server_packet_send(server, sock, SILC_PACKET_NEW_CHANNEL, broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, packet->data, packet->len, FALSE); diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 6a6733c6..86b0e6f5 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -860,6 +860,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) silc_server_perform_heartbeat, server->timeout_queue); + /* If we are router then announce our possible servers. */ + if (server->server_type == SILC_ROUTER) + silc_server_announce_servers(server); + + /* Announce our clients and channels to the router */ + silc_server_announce_clients(server); + silc_server_announce_channels(server); + out: /* Free the temporary connection data context */ if (sconn) @@ -1646,6 +1654,15 @@ void silc_server_packet_parse_type(SilcServer server, silc_server_new_id(server, sock, packet); break; + case SILC_PACKET_NEW_ID_LIST: + /* + * Received list of ID's. This packet is used by servers and routers + * to notify their primary router about clients and servers they have. + */ + SILC_LOG_DEBUG(("New ID List packet")); + silc_server_new_id_list(server, sock, packet); + break; + case SILC_PACKET_NEW_CLIENT: /* * Received new client packet. This includes client information that @@ -1693,6 +1710,8 @@ void silc_server_packet_parse_type(SilcServer server, * existing server or router connects to us and distributes information * of all channels it has. */ + SILC_LOG_DEBUG(("New Channel List packet")); + silc_server_new_channel_list(server, sock, packet); break; case SILC_PACKET_NEW_CHANNEL_USER_LIST: @@ -1701,6 +1720,8 @@ void silc_server_packet_parse_type(SilcServer server, * when existing server or router connects to us and distributes * information of all channel users it has. */ + SILC_LOG_DEBUG(("New Channel User List packet")); + silc_server_new_channel_user_list(server, sock, packet); break; case SILC_PACKET_REPLACE_ID: @@ -2219,6 +2240,51 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, return entry; } +/* Same as above but creates the channel with Channel ID `channel_id. */ + +SilcChannelEntry +silc_server_create_new_channel_with_id(SilcServer server, + char *cipher, + char *channel_name, + SilcChannelID *channel_id) +{ + SilcChannelEntry entry; + SilcCipher key; + + SILC_LOG_DEBUG(("Creating new channel")); + + if (!cipher) + cipher = "twofish"; + + /* Allocate cipher */ + silc_cipher_alloc(cipher, &key); + + channel_name = strdup(channel_name); + + /* Create the channel */ + entry = silc_idlist_add_channel(server->local_list, channel_name, + SILC_CHANNEL_MODE_NONE, channel_id, + NULL, key); + if (!entry) { + silc_free(channel_name); + return NULL; + } + + /* Now create the actual key material */ + silc_server_create_channel_key(server, entry, 16); + + /* Notify other routers about the new channel. We send the packet + to our primary route. */ + if (server->standalone == FALSE) { + silc_server_send_new_channel(server, server->router->connection, TRUE, + channel_name, entry->id, SILC_ID_CHANNEL_LEN); + } + + server->stat.my_channels++; + + return entry; +} + /* Generates new channel key. This is used to create the initial channel key but also to re-generate new key for channel. If `key_len' is provided it is the bytes of the key length. */ @@ -2360,3 +2426,264 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock, /* Send the heartbeat */ silc_server_send_heartbeat(hb->server, sock); } + +/* Returns assembled of all servers in the given ID list. The packet's + form is dictated by the New ID payload. */ + +static void silc_server_announce_get_servers(SilcServer server, + SilcIDList id_list, + SilcBuffer *servers) +{ + SilcIDCacheList list; + SilcIDCacheEntry id_cache; + SilcServerEntry entry; + SilcBuffer idp; + + /* Go through all clients in the list */ + if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, + SILC_ID_SERVER, &list)) { + if (silc_idcache_list_first(list, &id_cache)) { + while (id_cache) { + entry = (SilcServerEntry)id_cache->context; + + idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER); + + *servers = silc_buffer_realloc(*servers, + (*servers ? + (*servers)->truelen + idp->len : + idp->len)); + silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data)); + silc_buffer_put(*servers, idp->data, idp->len); + silc_buffer_pull(*servers, idp->len); + silc_buffer_free(idp); + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + + silc_idcache_list_free(list); + } +} + +/* This function is used by router to announce existing servers to our + primary router when we've connected to it. */ + +void silc_server_announce_servers(SilcServer server) +{ + SilcBuffer servers = NULL; + + SILC_LOG_DEBUG(("Announcing servers")); + + /* Get servers in local list */ + silc_server_announce_get_servers(server, server->local_list, &servers); + + /* Get servers in global list */ + silc_server_announce_get_servers(server, server->global_list, &servers); + + if (servers) { + silc_buffer_push(servers, servers->data - servers->head); + SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len); + + /* Send the packet */ + silc_server_packet_send(server, server->router->connection, + SILC_PACKET_NEW_ID_LIST, 0, + servers->data, servers->len, TRUE); + + silc_buffer_free(servers); + } +} + +/* Returns assembled packet of all clients in the given ID list. The + packet's form is dictated by the New ID Payload. */ + +static void silc_server_announce_get_clients(SilcServer server, + SilcIDList id_list, + SilcBuffer *clients) +{ + SilcIDCacheList list; + SilcIDCacheEntry id_cache; + SilcClientEntry client; + SilcBuffer idp; + + /* Go through all clients in the list */ + if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, + SILC_ID_CLIENT, &list)) { + if (silc_idcache_list_first(list, &id_cache)) { + while (id_cache) { + client = (SilcClientEntry)id_cache->context; + + idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); + + *clients = silc_buffer_realloc(*clients, + (*clients ? + (*clients)->truelen + idp->len : + idp->len)); + silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data)); + silc_buffer_put(*clients, idp->data, idp->len); + silc_buffer_pull(*clients, idp->len); + silc_buffer_free(idp); + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + + silc_idcache_list_free(list); + } +} + +/* This function is used to announce our existing clients to our router + when we've connected to it. */ + +void silc_server_announce_clients(SilcServer server) +{ + SilcBuffer clients = NULL; + + SILC_LOG_DEBUG(("Announcing clients")); + + /* Get clients in local list */ + silc_server_announce_get_clients(server, server->local_list, + &clients); + + /* As router we announce our global list as well */ + if (server->server_type == SILC_ROUTER) + silc_server_announce_get_clients(server, server->global_list, + &clients); + + if (clients) { + silc_buffer_push(clients, clients->data - clients->head); + SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len); + + /* Send the packet */ + silc_server_packet_send(server, server->router->connection, + SILC_PACKET_NEW_ID_LIST, 0, + clients->data, clients->len, TRUE); + + silc_buffer_free(clients); + } +} + +/* Returns assembled packets for all channels and users on those channels + from the given ID List. The packets are in the form dictated by the + New Channel and New Channel User payloads. */ + +static void silc_server_announce_get_channels(SilcServer server, + SilcIDList id_list, + SilcBuffer *channels, + SilcBuffer *channel_users) +{ + SilcIDCacheList list; + SilcIDCacheEntry id_cache; + SilcChannelEntry channel; + SilcChannelClientEntry chl; + unsigned char *cid; + unsigned short name_len; + int len; + + /* Go through all channels in the list */ + if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY, + SILC_ID_CHANNEL, &list)) { + if (silc_idcache_list_first(list, &id_cache)) { + while (id_cache) { + channel = (SilcChannelEntry)id_cache->context; + + cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + name_len = strlen(channel->channel_name); + + len = 4 + name_len + SILC_ID_CHANNEL_LEN; + *channels = + silc_buffer_realloc(*channels, + (*channels ? (*channels)->truelen + len : len)); + silc_buffer_pull_tail(*channels, + ((*channels)->end - (*channels)->data)); + silc_buffer_format(*channels, + SILC_STR_UI_SHORT(name_len), + SILC_STR_UI_XNSTRING(channel->channel_name, + name_len), + SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN), + SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN), + SILC_STR_END); + silc_buffer_pull(*channels, len); + + /* Now find all users on the channel */ + silc_list_start(channel->user_list); + while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) { + unsigned char *clid; + + clid = silc_id_id2str(chl->client->id, SILC_ID_CLIENT); + + len = 4 + SILC_ID_CHANNEL_LEN + SILC_ID_CLIENT_LEN; + *channel_users = + silc_buffer_realloc(*channel_users, + (*channel_users ? + (*channel_users)->truelen + len : len)); + silc_buffer_pull_tail(*channel_users, + ((*channel_users)->end - + (*channel_users)->data)); + silc_buffer_format(*channel_users, + SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN), + SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN), + SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN), + SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN), + SILC_STR_END); + silc_buffer_pull(*channel_users, len); + silc_free(clid); + } + + silc_free(cid); + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + + silc_idcache_list_free(list); + } +} + +/* This function is used to announce our existing channels to our router + when we've connected to it. This also announces the users on the + channels to the router. */ + +void silc_server_announce_channels(SilcServer server) +{ + SilcBuffer channels = NULL, channel_users = NULL; + + SILC_LOG_DEBUG(("Announcing channels and channel users")); + + /* Get channels and channel users in local list */ + silc_server_announce_get_channels(server, server->local_list, + &channels, &channel_users); + + /* Get channels and channel users in global list */ + silc_server_announce_get_channels(server, server->global_list, + &channels, &channel_users); + + if (channels) { + silc_buffer_push(channels, channels->data - channels->head); + SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len); + + /* Send the packet */ + silc_server_packet_send(server, server->router->connection, + SILC_PACKET_NEW_CHANNEL_LIST, 0, + channels->data, channels->len, + FALSE); + + silc_buffer_free(channels); + } + + if (channel_users) { + silc_buffer_push(channel_users, channel_users->data - channel_users->head); + SILC_LOG_HEXDUMP(("channel users"), channel_users->data, + channel_users->len); + + /* Send the packet */ + silc_server_packet_send(server, server->router->connection, + SILC_PACKET_NEW_CHANNEL_USER_LIST, 0, + channel_users->data, channel_users->len, + FALSE); + + silc_buffer_free(channel_users); + } +} diff --git a/apps/silcd/server.h b/apps/silcd/server.h index 3a041fc6..f8b7c6f7 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -111,6 +111,11 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcServerID *router_id, char *cipher, char *channel_name); +SilcChannelEntry +silc_server_create_new_channel_with_id(SilcServer server, + char *cipher, + char *channel_name, + SilcChannelID *channel_id); void silc_server_create_channel_key(SilcServer server, SilcChannelEntry channel, unsigned int key_len); @@ -119,5 +124,8 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, SilcChannelEntry channel); void silc_server_perform_heartbeat(SilcSocketConnection sock, void *hb_context); +void silc_server_announce_servers(SilcServer server); +void silc_server_announce_clients(SilcServer server); +void silc_server_announce_channels(SilcServer server); #endif diff --git a/doc/draft-riikonen-silc-pp-01.nroff b/doc/draft-riikonen-silc-pp-01.nroff index 110daec4..708dfe16 100644 --- a/doc/draft-riikonen-silc-pp-01.nroff +++ b/doc/draft-riikonen-silc-pp-01.nroff @@ -105,6 +105,7 @@ Table of Contents 2.3.25 Remove ID Payload .................................. 37 2.3.26 Remove Channel User Payload ........................ 38 2.3.27 Set Mode Payload ................................... XXX + 2.3.28 Set Mode List Payload .............................. XXX 2.4 SILC ID Types ............................................. 39 2.5 Packet Encryption And Decryption .......................... 39 2.5.1 Normal Packet Encryption And Decryption ............. 39 @@ -766,7 +767,17 @@ List of SILC Packet types are defined as follows. Payload of the packet: See section 2.3.27 Set Mode Payload - 32 SILC_PACKET_HEARTBEAT + 32 SILC_PACKET_SET_MODE_LIST + + This packet is used to distribute list of Set Mode payloads + from server to routers. This is equivalent to the packet + SILC_PACKET_SET_MODE except that it may include several + payloads. Client must not send this packet. + + Payload of the packet: See section 2.3.28 Set Mode List + Payload + + 33 SILC_PACKET_HEARTBEAT This packet is used by clients, servers and routers to keep the connection alive. It is recommended that all servers implement @@ -774,7 +785,7 @@ List of SILC Packet types are defined as follows. This packet does not have a payload. - 33 - 199 + 34 - 199 Currently undefined commands. @@ -1200,7 +1211,7 @@ ID's sent in arguments are sent inside ID Payload. only to the clients who is joined on the channel where the target client is on. - Max Arguments: 3 + Max Arguments: 3 Arguments: (1) (2) (3) @@ -1217,6 +1228,21 @@ ID's sent in arguments are sent inside ID Payload. Arguments: (1) The is the Message of the Day. + + +10 SILC_NOTIFY_TYPE_CHANNEL_CHANGE + + Sent when channel's ID has changed for a reason or another. This + is sent by noral server to the client. Client must change the + old Channel ID to the new one. This type must be sent only to the + clients who is joined on the channel. + + Max Arguments: 2 + Arguments: (1) (2) + + The is the channel's old ID and the + is the new one that must replace the old one. + .in 3 Notify types starting from 16384 are reserved for private notify @@ -1992,7 +2018,7 @@ o Client ID (variable length) - The Client ID of the client 2.3.22 New Channel List Payload This payload is used to distribute list of new channels from server -to routers. It might convenient to send list of new channels when +to routers. It might be convenient to send list of new channels when existing server connects to router, instead of sending them one by one. @@ -2010,14 +2036,14 @@ packet. They must not be sent in any other packet type. .ti 0 2.3.23 New Channel User List Payload -This payload is used to distribute list of channel users on specific -channel from server to routers. It might convenient to send list of -channel users when existing server connects to router, instead of -sending them one by one. +This payload is used to distribute list of channel users on a channel +from server to routers. It might convenient to send list of channel +users when existing server connects to router, instead of sending them +one by one. One list may include users for several different channels. There is no specific payload for this packet type. The packet type uses same payload as described in 2.3.20 New Channel User Payload. -To form a list several payloads is put in the packet each after each. +To form a list several payloads is put in the packet one after another. The payload is variable in length but can be calculated by calculating the length of the fields together. This forms one New Channel User Payload in the list. @@ -2235,6 +2261,26 @@ o Argument Nums (2 bytes) - Indicates the number of Argument .in 3 +.ti 0 +2.3.28 Set Mode List Payload + +This paylaod is used to distribute list of Set Mode payloads inside +one packet. When server announces channels and client's on those +channels to its primary router when it connects to it, it is convenient +to send list of Set Mode payloads to set the modes for the channel +and clients on those channel. One list may include several mode +types. + +There is no specific payload for this packet type. The packet type +uses same payload as described in 2.3.27 Set Mode Payload. To form a +list several payloads are put in the packet one after another. The +payload is variable in length but can be calculated by calculating the +length of the fields together. This forms one Set Mode payload in the +list. + +The list of payloads may only be sent with packet SILC_PACKET_SET_MODE_LIST. +They must not be sent in any other packet type. + .ti 0 2.4 SILC ID Types diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index fd17296d..c19f206e 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -1605,6 +1605,7 @@ void silc_client_notify_by_server(SilcClient client, silc_client_notify_by_server_pending, p); goto out; } + silc_free(client_id); /* Get old Client ID */ tmp = silc_argument_get_arg_type(args, 1, &tmp_len); @@ -1775,6 +1776,45 @@ void silc_client_notify_by_server(SilcClient client, /* Notify application */ client->ops->notify(client, conn, type, tmp); break; + + case SILC_NOTIFY_TYPE_CHANNEL_CHANGE: + /* + * Router has enforced a new ID to a channel. Let's change the old + * ID to the one provided here. + */ + + /* Get the old ID */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) + goto out; + channel_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!channel_id) + goto out; + + /* Get the channel entry */ + if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, + SILC_ID_CHANNEL, &id_cache)) + break; + + channel = (SilcChannelEntry)id_cache->context; + + /* Free the old ID */ + silc_free(channel_id); + silc_free(channel->id); + + /* Get the new ID */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (!tmp) + goto out; + channel->id = silc_id_payload_parse_id(tmp, tmp_len); + if (!channel->id) + goto out; + + id_cache->id = (void *)channel->id; + + /* Notify application */ + client->ops->notify(client, conn, type, channel, channel); + break; default: break; diff --git a/lib/silccore/id.h b/lib/silccore/id.h index abd89e0d..2cb84f00 100644 --- a/lib/silccore/id.h +++ b/lib/silccore/id.h @@ -100,6 +100,10 @@ typedef struct { #define SILC_ID_SERVER_COMPARE(id1, id2) \ SILC_ID_COMPARE(id1, id2, SILC_ID_SERVER_LEN) +/* Compares Channel ID's */ +#define SILC_ID_CHANNEL_COMPARE(id1, id2) \ + SILC_ID_COMPARE(id1, id2, SILC_ID_CHANNEL_LEN) + /* Compares IP addresses from the ID's. */ #define SILC_ID_COMPARE_IP(id1, id2) \ SILC_ID_COMPARE(id1, id2, 4) diff --git a/lib/silccore/silcnotify.h b/lib/silccore/silcnotify.h index 3362d7fc..3c85ed2a 100644 --- a/lib/silccore/silcnotify.h +++ b/lib/silccore/silcnotify.h @@ -40,6 +40,7 @@ typedef unsigned short SilcNotifyType; #define SILC_NOTIFY_TYPE_CMODE_CHANGE 7 /* "has changed channel mode" */ #define SILC_NOTIFY_TYPE_CUMODE_CHANGE 8 /* "has change mode" */ #define SILC_NOTIFY_TYPE_MOTD 9 /* message of the day */ +#define SILC_NOTIFY_TYPE_CHANNEL_CHANGE 10 /* Channel's ID has changed */ /* Prototypes */ SilcNotifyPayload silc_notify_payload_parse(SilcBuffer buffer); diff --git a/lib/silccore/silcpacket.h b/lib/silccore/silcpacket.h index 1a22736e..17edad02 100644 --- a/lib/silccore/silcpacket.h +++ b/lib/silccore/silcpacket.h @@ -223,7 +223,8 @@ typedef void (*SilcPacketParserCallback)(SilcPacketParserContext #define SILC_PACKET_REKEY 29 #define SILC_PACKET_REKEY_DONE 30 #define SILC_PACKET_SET_MODE 31 /* Set mode */ -#define SILC_PACKET_HEARTBEAT 32 /* Heartbeat */ +#define SILC_PACKET_SET_MODE_LIST 32 /* List of Set Mode's */ +#define SILC_PACKET_HEARTBEAT 33 /* Heartbeat */ /* #define SILC_PACKET_MAX 255 */ /* Macros */ diff --git a/lib/silcutil/silcbufutil.h b/lib/silcutil/silcbufutil.h index 6b41270a..9bd0cd6e 100644 --- a/lib/silcutil/silcbufutil.h +++ b/lib/silcutil/silcbufutil.h @@ -79,6 +79,9 @@ SilcBuffer silc_buffer_realloc(SilcBuffer sb, unsigned int newsize) { SilcBuffer sb_new; + if (!sb) + return silc_buffer_alloc(newsize); + sb_new = silc_buffer_alloc(newsize); silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new)); silc_buffer_put(sb_new, sb->head, sb->truelen);