X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fpacket_receive.c;h=65aba7318907e6c024dc0769da3c76a1570b54b2;hp=9321add3326ad09bd97862424c46e38531c8ae6a;hb=386c883d8774999c6e74d7c6c37e52e4163a4cb1;hpb=10abd339ccd4ef4b5540d2bee269c8edba4fd9e7 diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 9321add3..65aba731 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -27,343 +27,402 @@ extern char *server_version; -/* Received private message. This resolves the destination of the message - and sends the packet. This is used by both server and router. If the - destination is our locally connected client this sends the packet to - the client. This may also send the message for further routing if - the destination is not in our server (or router). */ +/* Received notify packet. Server can receive notify packets from router. + Server then relays the notify messages to clients if needed. */ -void silc_server_private_message(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_server_notify(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { - SilcClientID *id; - SilcServerEntry router; - SilcSocketConnection dst_sock; + SilcNotifyPayload payload; + SilcNotifyType type; + SilcArgumentPayload args; + SilcChannelID *channel_id = NULL, *channel_id2; + SilcClientID *client_id, *client_id2; + SilcServerID *server_id; + SilcChannelEntry channel; SilcClientEntry client; - SilcIDListData idata; + SilcServerEntry server_entry; + SilcChannelClientEntry chl; + SilcIDCacheEntry cache; + SilcHashTableList htl; + uint32 mode; + unsigned char *tmp; + uint32 tmp_len; + bool local; SILC_LOG_DEBUG(("Start")); + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER) + return; + if (!packet->dst_id) - goto err; + return; - /* Decode destination Client ID */ - id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT); - if (!id) { - SILC_LOG_ERROR(("Could not decode destination Client ID, dropped")); - goto err; + /* If the packet is destined directly to a client then relay the packet + before processing it. */ + if (packet->dst_id_type == SILC_ID_CLIENT) { + SilcIDListData idata; + SilcSocketConnection dst_sock; + + /* Get the route to the client */ + dst_sock = silc_server_get_client_route(server, packet->dst_id, + packet->dst_id_len, NULL, &idata); + if (dst_sock) + /* Relay the packet */ + silc_server_relay_packet(server, dst_sock, idata->send_key, + idata->hmac_receive, idata->psn_send++, + packet, TRUE); } - /* If the destination belongs to our server we don't have to route - the message anywhere but to send it to the local destination. */ - client = silc_idlist_find_client_by_id(server->local_list, id, NULL); - if (client) { - /* It exists, now deliver the message to the destination */ - dst_sock = (SilcSocketConnection)client->connection; - - /* If we are router and the client has router then the client is in - our cell but not directly connected to us. */ - if (server->server_type == SILC_ROUTER && client->router) { - /* We are of course in this case the client's router thus the real - "router" of the client is the server who owns the client. Thus - we will send the packet to that server. */ - router = (SilcServerEntry)client->router; - idata = (SilcIDListData)router; - - silc_server_send_private_message(server, router->connection, - idata->send_key, - idata->hmac, - packet); - return; - } - - /* Seems that client really is directly connected to us */ - idata = (SilcIDListData)client; - silc_server_send_private_message(server, dst_sock, - idata->send_key, - idata->hmac, packet); + /* Parse the Notify Payload */ + payload = silc_notify_payload_parse(packet->buffer->data, + packet->buffer->len); + if (!payload) return; - } - /* Destination belongs to someone not in this server. If we are normal - server our action is to send the packet to our router. */ - if (server->server_type == SILC_SERVER && !server->standalone) { - router = server->router; - - /* Send to primary route */ - if (router) { - dst_sock = (SilcSocketConnection)router->connection; - idata = (SilcIDListData)router; - silc_server_send_private_message(server, dst_sock, - idata->send_key, - idata->hmac, packet); - } - return; - } + /* If we are router and this packet is not already broadcast packet + we will broadcast it. The sending socket really cannot be router or + the router is buggy. If this packet is coming from router then it must + have the broadcast flag set already and we won't do anything. */ + if (!server->standalone && server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received Notify packet")); + if (packet->dst_id_type == SILC_ID_CHANNEL) { + /* Packet is destined to channel */ + channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, + packet->dst_id_type); + if (!channel_id) + goto out; - /* We are router and we will perform route lookup for the destination - and send the message to fastest route. */ - if (server->server_type == SILC_ROUTER && !server->standalone) { - /* Check first that the ID is valid */ - client = silc_idlist_find_client_by_id(server->global_list, id, NULL); - if (client) { - dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT); - router = (SilcServerEntry)dst_sock->user_data; - idata = (SilcIDListData)router; - - /* Get fastest route and send packet. */ - if (router) - silc_server_send_private_message(server, dst_sock, - idata->send_key, - idata->hmac, packet); - return; + silc_server_packet_send_dest(server, server->router->connection, + packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + channel_id, SILC_ID_CHANNEL, + packet->buffer->data, packet->buffer->len, + FALSE); + silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data, + packet->type, packet->flags, + channel_id, SILC_ID_CHANNEL, + packet->buffer->data, packet->buffer->len, + FALSE, TRUE); + } else { + /* Packet is destined to client or server */ + silc_server_packet_send(server, server->router->connection, + packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + packet->buffer->data, packet->buffer->len, + FALSE); + silc_server_backup_send(server, (SilcServerEntry)sock->user_data, + packet->type, packet->flags, + packet->buffer->data, packet->buffer->len, + FALSE, TRUE); } } - err: - silc_server_send_error(server, sock, - "No such nickname: Private message not sent"); -} - -/* Processes incoming command reply packet. The command reply packet may - be destined to one of our clients or it may directly for us. We will - call the command reply routine after processing the packet. */ + type = silc_notify_get_type(payload); + args = silc_notify_get_args(payload); + if (!args) + goto out; -void silc_server_command_reply(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcBuffer buffer = packet->buffer; - SilcClientEntry client = NULL; - SilcSocketConnection dst_sock; - SilcIDListData idata; - SilcClientID *id = NULL; + switch(type) { + case SILC_NOTIFY_TYPE_JOIN: + /* + * Distribute the notify to local clients on the channel + */ + SILC_LOG_DEBUG(("JOIN notify")); - SILC_LOG_DEBUG(("Start")); + /* Get Channel 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; - /* Source must be server or router */ - if (packet->src_id_type != SILC_ID_SERVER && - sock->type != SILC_SOCKET_TYPE_ROUTER) - return; + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } + } + silc_free(channel_id); - if (packet->dst_id_type == SILC_ID_CHANNEL) - return; + /* Get client ID */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) + goto out; + client_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!client_id) + goto out; - if (packet->dst_id_type == SILC_ID_CLIENT) { - /* Destination must be one of ours */ - id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT); - if (!id) - return; - client = silc_idlist_find_client_by_id(server->local_list, id, NULL); + /* If the the client is not in local list we check global list (ie. the + channel will be global channel) and if it does not exist then create + entry for the client. */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, server->server_type, + NULL); if (!client) { - SILC_LOG_ERROR(("Cannot process command reply to unknown client")); - silc_free(id); - return; + client = silc_idlist_find_client_by_id(server->local_list, + client_id, server->server_type, + NULL); + if (!client) { + /* If router did not find the client the it is bogus */ + if (server->server_type != SILC_SERVER) + goto out; + + client = + silc_idlist_add_client(server->global_list, NULL, NULL, NULL, + silc_id_dup(client_id, SILC_ID_CLIENT), + sock->user_data, NULL, 0); + if (!client) { + SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + silc_free(client_id); + goto out; + } + + client->data.status |= SILC_IDLIST_STATUS_REGISTERED; + } } - } - if (packet->dst_id_type == SILC_ID_SERVER) { - /* For now this must be for us */ - if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) { - SILC_LOG_ERROR(("Cannot process command reply to unknown server")); - return; + /* Do not process the notify if the client is not registered */ + if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) + break; + + /* Do not add client to channel if it is there already */ + if (silc_server_client_on_channel(client, channel)) { + SILC_LOG_DEBUG(("Client already on channel")); + break; } - } - /* Execute command reply locally for the command */ - silc_server_command_reply_process(server, sock, buffer); + /* Send to channel */ + silc_server_packet_send_to_channel(server, sock, channel, packet->type, + FALSE, packet->buffer->data, + packet->buffer->len, FALSE); - if (packet->dst_id_type == SILC_ID_CLIENT && client && id) { - /* Relay the packet to the client */ - - dst_sock = (SilcSocketConnection)client->connection; - silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len - + packet->dst_id_len + packet->padlen); - - silc_packet_send_prepare(dst_sock, 0, 0, buffer->len); - silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len); - - idata = (SilcIDListData)client; - - /* Encrypt packet */ - silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, - buffer->len); - - /* Send the packet */ - silc_server_packet_send_real(server, dst_sock, TRUE); + if (server->server_type != SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_ROUTER) + /* The channel is global now */ + channel->global_users = TRUE; - silc_free(id); - } -} + SILC_LOG_DEBUG(("Joining to channel %s", channel->channel_name)); -/* Process received channel message. The message can be originated from - client or server. */ + /* JOIN the global client to the channel (local clients (if router + created the channel) is joined in the pending JOIN command). */ + chl = silc_calloc(1, sizeof(*chl)); + chl->client = client; + chl->channel = channel; -void silc_server_channel_message(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcChannelEntry channel = NULL; - SilcChannelClientEntry chl; - SilcChannelID *id = NULL; - void *sender = NULL; + /* If this is the first one on the channel then it is the founder of + the channel. */ + if (!silc_hash_table_count(channel->user_list)) + chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO); - SILC_LOG_DEBUG(("Processing channel message")); + silc_hash_table_add(channel->user_list, client, chl); + silc_hash_table_add(client->channels, channel, chl); + silc_free(client_id); + channel->user_count++; - /* Sanity checks */ - if (packet->dst_id_type != SILC_ID_CHANNEL) { - SILC_LOG_DEBUG(("Received bad message for channel, dropped")); - goto out; - } + break; - /* Find channel entry */ - id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL); - if (!id) - goto out; - channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL); - if (!channel) { - channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL); - if (!channel) { - SILC_LOG_DEBUG(("Could not find channel")); - goto out; + case SILC_NOTIFY_TYPE_LEAVE: + /* + * Distribute the notify to local clients on the channel + */ + SILC_LOG_DEBUG(("LEAVE notify")); + + if (!channel_id) { + channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, + packet->dst_id_type); + if (!channel_id) + goto out; } - } - /* See that this client is on the channel. If the message is coming - from router we won't do the check as the message is from client that - we don't know about. Also, if the original sender is not client - (as it can be server as well) we don't do the check. */ - sender = silc_id_str2id(packet->src_id, packet->src_id_len, - packet->src_id_type); - if (!sender) - goto out; - if (sock->type != SILC_SOCKET_TYPE_ROUTER && - packet->src_id_type == SILC_ID_CLIENT) { - silc_list_start(channel->user_list); - while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) { - if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender)) - break; + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } } - if (chl == SILC_LIST_END) + + /* Get client ID */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) { + silc_free(channel_id); goto out; - } + } + client_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!client_id) { + silc_free(channel_id); + goto out; + } - /* Distribute the packet to our local clients. This will send the - packet for further routing as well, if needed. */ - silc_server_packet_relay_to_channel(server, sock, channel, sender, - packet->src_id_type, - packet->buffer->data, - packet->buffer->len, FALSE); + /* Get client entry */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!client) { + client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, NULL); + if (!client) { + silc_free(client_id); + silc_free(channel_id); + goto out; + } + } + silc_free(client_id); - out: - if (sender) - silc_free(sender); - if (id) - silc_free(id); -} + /* Check if on channel */ + if (!silc_server_client_on_channel(client, channel)) + break; -/* Received channel key packet. We distribute the key to all of our locally - connected clients on the channel. */ + /* Send the leave notify to channel */ + silc_server_packet_send_to_channel(server, sock, channel, packet->type, + FALSE, packet->buffer->data, + packet->buffer->len, FALSE); -void silc_server_channel_key(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcBuffer buffer = packet->buffer; - SilcChannelEntry channel; + /* Remove the user from channel */ + silc_server_remove_from_one_channel(server, sock, channel, client, FALSE); + break; - if (packet->src_id_type != SILC_ID_SERVER) - return; + case SILC_NOTIFY_TYPE_SIGNOFF: + /* + * Distribute the notify to local clients on the channel + */ + SILC_LOG_DEBUG(("SIGNOFF notify")); - /* Save the channel key */ - channel = silc_server_save_channel_key(server, buffer, NULL); - if (!channel) - return; + /* Get client ID */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) + goto out; + client_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!client_id) + goto out; - /* Distribute the key to everybody who is on the channel. If we are router - we will also send it to locally connected servers. */ - silc_server_send_channel_key(server, sock, channel, FALSE); -} + /* Get client entry */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, &cache); + if (!client) { + client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, &cache); + if (!client) { + silc_free(client_id); + goto out; + } + } + silc_free(client_id); -/* Received packet to replace a ID. This checks that the requested ID - exists and replaces it with the new one. */ + /* Get signoff message */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (tmp_len > 128) + tmp = NULL; -void silc_server_replace_id(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcBuffer buffer = packet->buffer; - unsigned char *old_id = NULL, *new_id = NULL; - SilcIdType old_id_type, new_id_type; - unsigned short old_id_len, new_id_len; - void *id = NULL, *id2 = NULL; - int ret; + /* Update statistics */ + server->stat.clients--; + if (server->server_type == SILC_ROUTER) + server->stat.cell_clients--; + SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR); + SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type == SILC_ID_CLIENT) - return; + /* Remove the client from all channels. */ + silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE); - SILC_LOG_DEBUG(("Replacing ID")); + client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; + cache->expire = SILC_ID_CACHE_EXPIRE_DEF; + break; - ret = silc_buffer_unformat(buffer, - SILC_STR_UI_SHORT(&old_id_type), - SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len), - SILC_STR_UI_SHORT(&new_id_type), - SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len), - SILC_STR_END); - if (ret == -1) - goto out; + case SILC_NOTIFY_TYPE_TOPIC_SET: + /* + * Distribute the notify to local clients on the channel + */ - if (old_id_type != new_id_type) - goto out; + SILC_LOG_DEBUG(("TOPIC SET notify")); - if (old_id_len != silc_id_get_len(old_id_type) || - new_id_len != silc_id_get_len(new_id_type)) - goto out; + if (!channel_id) { + channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, + packet->dst_id_type); + if (!channel_id) + goto out; + } - id = silc_id_str2id(old_id, old_id_len, old_id_type); - if (!id) - goto out; + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } + } - id2 = silc_id_str2id(new_id, new_id_len, new_id_type); - if (!id2) - goto out; + /* Get the topic */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (!tmp) { + silc_free(channel_id); + goto out; + } - /* If we are router and this packet is not already broadcast packet - we will broadcast it. The sending socket really cannot be router or - the router is buggy. If this packet is coming from router then it must - have the broadcast flag set already and we won't do anything. */ - if (!server->standalone && server->server_type == SILC_ROUTER && - sock->type == SILC_SOCKET_TYPE_SERVER && - !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received Replace ID packet")); - silc_server_packet_send(server, server->router->connection, packet->type, - packet->flags | SILC_PACKET_FLAG_BROADCAST, - buffer->data, buffer->len, FALSE); - } + silc_free(channel->topic); + channel->topic = strdup(tmp); - /* Replace the old ID */ - switch(old_id_type) { - case SILC_ID_CLIENT: + /* Send the same notify to the channel */ + silc_server_packet_send_to_channel(server, sock, channel, packet->type, + FALSE, packet->buffer->data, + packet->buffer->len, FALSE); + silc_free(channel_id); + break; + + case SILC_NOTIFY_TYPE_NICK_CHANGE: { - SilcBuffer nidp, oidp; - SilcClientEntry client = NULL; + /* + * Distribute the notify to local clients on the channel + */ + unsigned char *id, *id2; + SILC_LOG_DEBUG(("NICK CHANGE notify")); + + /* Get old client ID */ + id = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!id) + goto out; + client_id = silc_id_payload_parse_id(id, tmp_len); + if (!client_id) + goto out; + + /* Get new client ID */ + id2 = silc_argument_get_arg_type(args, 2, &tmp_len); + if (!id2) + goto out; + client_id2 = silc_id_payload_parse_id(id2, tmp_len); + if (!client_id2) + goto out; + SILC_LOG_DEBUG(("Old Client ID id(%s)", - silc_id_render(id, SILC_ID_CLIENT))); + silc_id_render(client_id, SILC_ID_CLIENT))); SILC_LOG_DEBUG(("New Client ID id(%s)", - silc_id_render(id2, SILC_ID_CLIENT))); + silc_id_render(client_id2, SILC_ID_CLIENT))); - if ((client = silc_idlist_replace_client_id(server->local_list, - id, id2)) == NULL) - if (server->server_type == SILC_ROUTER) - client = silc_idlist_replace_client_id(server->global_list, id, id2); - - if (client) { - oidp = silc_id_payload_encode(id, SILC_ID_CLIENT); - nidp = silc_id_payload_encode(id2, SILC_ID_CLIENT); + /* Replace the Client ID */ + client = silc_idlist_replace_client_id(server->global_list, client_id, + client_id2); + if (!client) + client = silc_idlist_replace_client_id(server->local_list, client_id, + client_id2); + if (client) { /* The nickname is not valid anymore, set it NULL. This causes that the nickname will be queried if someone wants to know it. */ if (client->nickname) @@ -372,617 +431,780 @@ void silc_server_replace_id(SilcServer server, /* Send the NICK_CHANGE notify type to local clients on the channels this client is joined to. */ - silc_server_send_notify_on_channels(server, client, + silc_server_send_notify_on_channels(server, NULL, client, SILC_NOTIFY_TYPE_NICK_CHANGE, 2, - oidp->data, oidp->len, - nidp->data, nidp->len); - - silc_buffer_free(nidp); - silc_buffer_free(oidp); + id, tmp_len, + id2, tmp_len); } + + silc_free(client_id); + if (!client) + silc_free(client_id2); break; } - case SILC_ID_SERVER: - SILC_LOG_DEBUG(("Old Server ID id(%s)", - silc_id_render(id, SILC_ID_SERVER))); - SILC_LOG_DEBUG(("New Server ID id(%s)", - 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: - 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; + case SILC_NOTIFY_TYPE_CMODE_CHANGE: + /* + * Distribute the notify to local clients on the channel + */ + + SILC_LOG_DEBUG(("CMODE CHANGE notify")); + + if (!channel_id) { + channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, + packet->dst_id_type); + if (!channel_id) + goto out; + } - default: - silc_free(id2); - break; - } + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } + } - out: - if (id) - silc_free(id); - if (old_id) - silc_free(old_id); - if (new_id) - silc_free(new_id); -} + /* Get the mode */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (!tmp) { + silc_free(channel_id); + goto out; + } + SILC_GET32_MSB(mode, tmp); -/* Received New Client packet and processes it. Creates Client ID for the - client. Client becomes registered after calling this functions. */ + /* Check if mode changed */ + if (channel->mode == mode) + break; -SilcClientEntry silc_server_new_client(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcBuffer buffer = packet->buffer; - SilcClientEntry client; - SilcIDCacheEntry cache; - SilcClientID *client_id; - SilcBuffer reply; - SilcIDListData idata; - char *username = NULL, *realname = NULL, *id_string; - int ret; + /* Send the same notify to the channel */ + silc_server_packet_send_to_channel(server, sock, channel, packet->type, + FALSE, packet->buffer->data, + packet->buffer->len, FALSE); - SILC_LOG_DEBUG(("Creating new client")); + /* If the channel had private keys set and the mode was removed then + we must re-generate and re-distribute a new channel key */ + if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY && + !(mode & SILC_CHANNEL_MODE_PRIVKEY)) { + /* Re-generate channel key */ + if (!silc_server_create_channel_key(server, channel, 0)) + goto out; + + /* Send the channel key. This sends it to our local clients and if + we are normal server to our router as well. */ + silc_server_send_channel_key(server, NULL, channel, + server->server_type == SILC_ROUTER ? + FALSE : !server->standalone); + } - if (sock->type != SILC_SOCKET_TYPE_CLIENT) - return NULL; + /* Change mode */ + channel->mode = mode; + silc_free(channel_id); - /* Take client entry */ - client = (SilcClientEntry)sock->user_data; - idata = (SilcIDListData)client; + /* Get the hmac */ + tmp = silc_argument_get_arg_type(args, 4, &tmp_len); + if (tmp) { + unsigned char hash[32]; - /* Fetch the old client cache entry so that we can update it. */ - if (!silc_idcache_find_by_context(server->local_list->clients, - sock->user_data, &cache)) { - SILC_LOG_ERROR(("Lost client's cache entry - bad thing")); - return NULL; - } + if (channel->hmac) + silc_hmac_free(channel->hmac); + if (!silc_hmac_alloc(tmp, NULL, &channel->hmac)) + goto out; - /* Parse incoming packet */ - ret = silc_buffer_unformat(buffer, - SILC_STR_UI16_STRING_ALLOC(&username), - SILC_STR_UI16_STRING_ALLOC(&realname), - SILC_STR_END); - if (ret == -1) { - if (username) - silc_free(username); - if (realname) - silc_free(realname); - return NULL; - } + /* Set the HMAC key out of current channel key. The client must do + this locally. */ + silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, + channel->key_len / 8, + hash); + silc_hmac_set_key(channel->hmac, hash, + silc_hash_len(silc_hmac_get_hash(channel->hmac))); + memset(hash, 0, sizeof(hash)); + } - /* Create Client ID */ - silc_id_create_client_id(server->id, server->rng, server->md5hash, - username, &client_id); + /* Get the passphrase */ + tmp = silc_argument_get_arg_type(args, 5, &tmp_len); + if (tmp) { + silc_free(channel->passphrase); + channel->passphrase = strdup(tmp); + } - /* Update client entry */ - idata->registered = TRUE; - client->nickname = strdup(username); - client->username = username; - client->userinfo = realname; - client->id = client_id; + break; - /* Update the cache entry */ - cache->id = (void *)client_id; - cache->type = SILC_ID_CLIENT; - cache->data = username; - silc_idcache_sort_by_data(server->local_list->clients); + case SILC_NOTIFY_TYPE_CUMODE_CHANGE: + { + /* + * Distribute the notify to local clients on the channel + */ + SilcChannelClientEntry chl2 = NULL; + bool notify_sent = FALSE; + + SILC_LOG_DEBUG(("CUMODE CHANGE notify")); + + if (!channel_id) { + channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, + packet->dst_id_type); + if (!channel_id) + goto out; + } - /* Notify our router about new client on the SILC network */ - if (!server->standalone) - silc_server_send_new_id(server, (SilcSocketConnection) - server->router->connection, - server->server_type == SILC_ROUTER ? TRUE : FALSE, - client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN); - - /* Send the new client ID to the client. */ - id_string = silc_id_id2str(client->id, SILC_ID_CLIENT); - reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN); - silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply)); - silc_buffer_format(reply, - SILC_STR_UI_SHORT(SILC_ID_CLIENT), - SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN), - SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN), - SILC_STR_END); - silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, - reply->data, reply->len, FALSE); - silc_free(id_string); - silc_buffer_free(reply); + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } + } - /* Send some nice info to the client */ - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Welcome to the SILC Network %s@%s", - username, sock->hostname)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Your host is %s, running version %s", - server->config->server_info->server_name, - server_version)); - if (server->server_type == SILC_ROUTER) { - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("There are %d clients on %d servers in SILC " - "Network", server->stat.clients, - server->stat.servers + 1)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("There are %d clients on %d server in our cell", - server->stat.cell_clients, - server->stat.cell_servers + 1)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("I have %d clients, %d channels, %d servers and " - "%d routers", - server->stat.my_clients, - server->stat.my_channels, - server->stat.my_servers, - server->stat.my_routers)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("%d server operators and %d router operators " - "online", - server->stat.my_server_ops, - server->stat.my_router_ops)); - } else { - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("I have %d clients and %d channels formed", - server->stat.my_clients, - server->stat.my_channels)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("%d operators online", - server->stat.my_server_ops)); - } - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Your connection is secured with %s cipher, " - "key length %d bits", - idata->send_key->cipher->name, - idata->send_key->cipher->key_len)); - SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("Your current nickname is %s", - client->nickname)); - - /* Send motd */ - silc_server_send_motd(server, sock); + /* Get the mode */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (!tmp) { + silc_free(channel_id); + goto out; + } + + SILC_GET32_MSB(mode, tmp); + + /* Get target client */ + tmp = silc_argument_get_arg_type(args, 3, &tmp_len); + if (!tmp) + goto out; + client_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!client_id) + goto out; + + /* Get client entry */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!client) { + client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, NULL); + if (!client) { + silc_free(client_id); + goto out; + } + } + silc_free(client_id); - return client; -} + /* Get entry to the channel user list */ + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chl)) { + /* If the mode is channel founder and we already find a client + to have that mode on the channel we will enforce the sender + to change the channel founder mode away. There can be only one + channel founder on the channel. */ + if (server->server_type == SILC_ROUTER && + mode & SILC_CHANNEL_UMODE_CHANFO && + chl->mode & SILC_CHANNEL_UMODE_CHANFO) { + SilcBuffer idp; + unsigned char cumode[4]; + + if (chl->client == client && chl->mode == mode) { + notify_sent = TRUE; + break; + } + + mode &= ~SILC_CHANNEL_UMODE_CHANFO; + silc_server_send_notify_cumode(server, sock, FALSE, channel, mode, + client->id, SILC_ID_CLIENT, + client->id); + + idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); + SILC_PUT32_MSB(mode, cumode); + silc_server_send_notify_to_channel(server, sock, channel, FALSE, + SILC_NOTIFY_TYPE_CUMODE_CHANGE, + 3, idp->data, idp->len, + cumode, 4, + idp->data, idp->len); + silc_buffer_free(idp); + notify_sent = TRUE; + + /* Force the mode change if we alredy set the mode */ + if (chl2) { + chl2->mode = mode; + silc_free(channel_id); + silc_hash_table_list_reset(&htl); + goto out; + } + } + + if (chl->client == client) { + if (chl->mode == mode) { + notify_sent = TRUE; + break; + } + + SILC_LOG_DEBUG(("Changing the channel user mode")); + + /* Change the mode */ + chl->mode = mode; + if (!(mode & SILC_CHANNEL_UMODE_CHANFO)) + break; + + chl2 = chl; + } + } + silc_hash_table_list_reset(&htl); + + /* Send the same notify to the channel */ + if (!notify_sent) + silc_server_packet_send_to_channel(server, sock, channel, + packet->type, + FALSE, packet->buffer->data, + packet->buffer->len, FALSE); + + silc_free(channel_id); + break; + } -/* Create new server. This processes received New Server packet and - saves the received Server ID. The server is our locally connected - server thus we save all the information and save it to local list. - This funtion can be used by both normal server and router server. - If normal server uses this it means that its router has connected - to the server. If router uses this it means that one of the cell's - servers is connected to the router. */ + case SILC_NOTIFY_TYPE_INVITE: -SilcServerEntry silc_server_new_server(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcBuffer buffer = packet->buffer; - SilcServerEntry new_server; - SilcIDCacheEntry cache; - SilcServerID *server_id; - SilcIDListData idata; - unsigned char *server_name, *id_string; - unsigned short id_len; - int ret; + if (packet->dst_id_type == SILC_ID_CLIENT) + goto out; - SILC_LOG_DEBUG(("Creating new server")); + SILC_LOG_DEBUG(("INVITE notify")); - if (sock->type != SILC_SOCKET_TYPE_SERVER && - sock->type != SILC_SOCKET_TYPE_ROUTER) - return NULL; + /* Get Channel 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; - /* Take server entry */ - new_server = (SilcServerEntry)sock->user_data; - idata = (SilcIDListData)new_server; + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } + } + silc_free(channel_id); - /* Fetch the old server cache entry so that we can update it. */ - if (!silc_idcache_find_by_context(server->local_list->servers, - sock->user_data, &cache)) { - SILC_LOG_ERROR(("Lost server's cache entry - bad thing")); - return NULL; - } + /* Get the added invite */ + tmp = silc_argument_get_arg_type(args, 3, &tmp_len); + if (tmp) { + if (!channel->invite_list) + channel->invite_list = silc_calloc(tmp_len + 2, + sizeof(*channel->invite_list)); + else + channel->invite_list = silc_realloc(channel->invite_list, + sizeof(*channel->invite_list) * + (tmp_len + + strlen(channel->invite_list) + + 2)); + if (tmp[tmp_len - 1] == ',') + tmp[tmp_len - 1] = '\0'; + + strncat(channel->invite_list, tmp, tmp_len); + strncat(channel->invite_list, ",", 1); + } - /* Parse the incoming packet */ - ret = silc_buffer_unformat(buffer, - SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len), - SILC_STR_UI16_STRING_ALLOC(&server_name), - SILC_STR_END); - if (ret == -1) { - if (id_string) - silc_free(id_string); - if (server_name) - silc_free(server_name); - return NULL; - } + /* Get the deleted invite */ + tmp = silc_argument_get_arg_type(args, 4, &tmp_len); + if (tmp && channel->invite_list) { + char *start, *end, *n; + + if (!strncmp(channel->invite_list, tmp, + strlen(channel->invite_list) - 1)) { + silc_free(channel->invite_list); + channel->invite_list = NULL; + } else { + start = strstr(channel->invite_list, tmp); + if (start && strlen(start) >= tmp_len) { + end = start + tmp_len; + n = silc_calloc(strlen(channel->invite_list) - tmp_len, sizeof(*n)); + strncat(n, channel->invite_list, start - channel->invite_list); + strncat(n, end + 1, ((channel->invite_list + + strlen(channel->invite_list)) - end) - 1); + silc_free(channel->invite_list); + channel->invite_list = n; + } + } + } - if (id_len > buffer->len) { - silc_free(id_string); - silc_free(server_name); - return NULL; - } + break; - /* Get Server ID */ - server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER); - if (!server_id) { - silc_free(id_string); - silc_free(server_name); - return NULL; - } - silc_free(id_string); + case SILC_NOTIFY_TYPE_CHANNEL_CHANGE: + /* + * Distribute to the local clients on the channel and change the + * channel ID. + */ - /* Update client entry */ - idata->registered = TRUE; - new_server->server_name = server_name; - new_server->id = server_id; + SILC_LOG_DEBUG(("CHANNEL CHANGE")); - /* Update the cache entry */ - cache->id = (void *)server_id; - cache->type = SILC_ID_SERVER; - cache->data = server_name; - silc_idcache_sort_by_data(server->local_list->servers); + if (sock->type != SILC_SOCKET_TYPE_ROUTER) + break; - /* Distribute the information about new server in the SILC network - to our router. If we are normal server we won't send anything - since this connection must be our router connection. */ - if (server->server_type == SILC_ROUTER && !server->standalone && - server->router->connection != sock) - silc_server_send_new_id(server, server->router->connection, - TRUE, new_server->id, SILC_ID_SERVER, - SILC_ID_SERVER_LEN); + /* Get the old Channel 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; - if (server->server_type == SILC_ROUTER) - server->stat.cell_servers++; + /* Get the channel entry */ + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } + } - return new_server; -} + /* Send the notify to the channel */ + silc_server_packet_send_to_channel(server, sock, channel, packet->type, + FALSE, packet->buffer->data, + packet->buffer->len, FALSE); -/* Processes incoming New ID packet. New ID Payload is used to distribute - information about newly registered clients and servers. */ + /* Get the new Channel ID */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (!tmp) + goto out; + channel_id2 = silc_id_payload_parse_id(tmp, tmp_len); + if (!channel_id2) + goto out; -void silc_server_new_id(SilcServer server, SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcBuffer buffer = packet->buffer; - SilcIDList id_list; - SilcServerEntry router; - SilcSocketConnection router_sock; - SilcIDPayload idp; - SilcIdType id_type; - unsigned char *hash = NULL; - void *id; + SILC_LOG_DEBUG(("Old Channel ID id(%s)", + silc_id_render(channel_id, SILC_ID_CHANNEL))); + SILC_LOG_DEBUG(("New Channel ID id(%s)", + silc_id_render(channel_id2, SILC_ID_CHANNEL))); + + /* Replace the Channel ID */ + if (!silc_idlist_replace_channel_id(server->local_list, channel_id, + channel_id2)) + if (!silc_idlist_replace_channel_id(server->global_list, channel_id, + channel_id2)) { + silc_free(channel_id2); + channel_id2 = NULL; + } - SILC_LOG_DEBUG(("Processing new ID")); + if (channel_id2) { + SilcBuffer users = NULL, users_modes = NULL; + + /* Re-announce this channel which ID was changed. */ + silc_server_send_new_channel(server, sock, FALSE, channel->channel_name, + channel->id, + silc_id_get_len(channel->id, + SILC_ID_CHANNEL), + channel->mode); + + /* Re-announce our clients on the channel as the ID has changed now */ + silc_server_announce_get_channel_users(server, channel, &users, + &users_modes); + if (users) { + silc_buffer_push(users, users->data - users->head); + silc_server_packet_send(server, sock, + SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, + users->data, users->len, FALSE); + silc_buffer_free(users); + } + if (users_modes) { + silc_buffer_push(users_modes, users_modes->data - users_modes->head); + silc_server_packet_send_dest(server, sock, + SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, + channel->id, SILC_ID_CHANNEL, + users_modes->data, + users_modes->len, FALSE); + silc_buffer_free(users_modes); + } - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - server->server_type == SILC_SERVER || - packet->src_id_type != SILC_ID_SERVER) - return; + /* Re-announce channel's topic */ + if (channel->topic) { + silc_server_send_notify_topic_set(server, sock, + server->server_type == SILC_ROUTER ? + TRUE : FALSE, channel, + channel->id, SILC_ID_CHANNEL, + channel->topic); + } + } - idp = silc_id_payload_parse(buffer); - if (!idp) - return; + silc_free(channel_id); - id_type = silc_id_payload_get_type(idp); + break; - /* Normal server cannot have other normal server connections */ - if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER) - goto out; + case SILC_NOTIFY_TYPE_SERVER_SIGNOFF: + /* + * Remove the server entry and all clients that this server owns. + */ - id = silc_id_payload_get_id(idp); - if (!id) - goto out; + SILC_LOG_DEBUG(("SERVER SIGNOFF notify")); - /* If the sender of this packet is server and we are router we need to - broadcast this packet to other routers in the network. */ - if (!server->standalone && server->server_type == SILC_ROUTER && - sock->type == SILC_SOCKET_TYPE_SERVER && - !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received New ID packet")); - silc_server_packet_send(server, server->router->connection, - packet->type, - packet->flags | SILC_PACKET_FLAG_BROADCAST, - buffer->data, buffer->len, FALSE); - } + /* Get Server ID */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) + goto out; + server_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!server_id) + goto out; - if (sock->type == SILC_SOCKET_TYPE_SERVER) - id_list = server->local_list; - else - id_list = server->global_list; + /* Get server entry */ + server_entry = silc_idlist_find_server_by_id(server->global_list, + server_id, TRUE, NULL); + local = TRUE; + if (!server_entry) { + server_entry = silc_idlist_find_server_by_id(server->local_list, + server_id, TRUE, NULL); + local = TRUE; + if (!server_entry) { + /* If we are normal server then we might not have the server. Check + whether router was kind enough to send the list of all clients + that actually was to be removed. Remove them if the list is + available. */ + if (server->server_type != SILC_ROUTER && + silc_argument_get_arg_num(args) > 1) { + int i; + + for (i = 1; i < silc_argument_get_arg_num(args); i++) { + /* Get Client ID */ + tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len); + if (!tmp) + continue; + client_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!client_id) + continue; + + /* Get client entry */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, &cache); + local = TRUE; + if (!client) { + client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, &cache); + local = FALSE; + if (!client) { + silc_free(client_id); + continue; + } + } + silc_free(client_id); + + /* Update statistics */ + server->stat.clients--; + if (server->server_type == SILC_ROUTER) + server->stat.cell_clients--; + SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR); + SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); + + /* Remove the client from all channels. */ + silc_server_remove_from_channels(server, NULL, client, + TRUE, NULL, FALSE); + + /* Remove the client */ + silc_idlist_del_client(local ? server->local_list : + server->global_list, client); + } + } - router_sock = sock; - router = sock->user_data; + silc_free(server_id); + goto out; + } + } + silc_free(server_id); - switch(id_type) { - case SILC_ID_CLIENT: - { - SilcClientEntry entry; + /* Free all client entries that this server owns as they will + become invalid now as well. */ + silc_server_remove_clients_by_server(server, server_entry, TRUE); - SILC_LOG_DEBUG(("New client id(%s) from [%s] %s", - silc_id_render(id, SILC_ID_CLIENT), - sock->type == SILC_SOCKET_TYPE_SERVER ? - "Server" : "Router", sock->hostname)); - - /* As a router we keep information of all global information in our - global list. Cell wide information however is kept in the local - list. The client is put to global list and we will take the hash - value of the Client ID and save it to the ID Cache system for fast - searching in the future. */ - hash = silc_calloc(sizeof(((SilcClientID *)id)->hash), - sizeof(unsigned char)); - memcpy(hash, ((SilcClientID *)id)->hash, - sizeof(((SilcClientID *)id)->hash)); - entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id, - router, NULL); - entry->nickname = NULL; + /* Remove the server entry */ + silc_idlist_del_server(local ? server->local_list : + server->global_list, server_entry); - if (sock->type == SILC_SOCKET_TYPE_SERVER) - server->stat.cell_clients++; - server->stat.clients++; + /* XXX update statistics */ -#if 0 - /* XXX Adding two ID's with same IP number replaces the old entry thus - gives wrong route. Thus, now disabled until figured out a better way - to do this or when removed the whole thing. This could be removed - because entry->router->connection gives always the most optimal route - for the ID anyway (unless new routes (faster perhaps) are established - after receiving this ID, this we don't know however). */ - /* Add route cache for this ID */ - silc_server_route_add(silc_server_route_hash( - ((SilcClientID *)id)->ip.s_addr, - server->id->port), ((SilcClientID *)id)->ip.s_addr, - router); -#endif - } break; - case SILC_ID_SERVER: - SILC_LOG_DEBUG(("New server id(%s) from [%s] %s", - silc_id_render(id, SILC_ID_SERVER), - sock->type == SILC_SOCKET_TYPE_SERVER ? - "Server" : "Router", sock->hostname)); + case SILC_NOTIFY_TYPE_KICKED: + /* + * Distribute the notify to local clients on the channel + */ - /* As a router we keep information of all global information in our global - list. Cell wide information however is kept in the local list. */ - silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock); - - if (sock->type == SILC_SOCKET_TYPE_SERVER) - server->stat.cell_servers++; - server->stat.servers++; + SILC_LOG_DEBUG(("KICKED notify")); + + if (!channel_id) { + channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, + packet->dst_id_type); + if (!channel_id) + goto out; + } -#if 0 - /* Add route cache for this ID */ - silc_server_route_add(silc_server_route_hash( - ((SilcServerID *)id)->ip.s_addr, - ((SilcServerID *)id)->port), - ((SilcServerID *)id)->ip.s_addr, - router); -#endif - break; - - case SILC_ID_CHANNEL: - SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet")); - break; + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; + } + } + silc_free(channel_id); - default: - break; - } + /* Get client ID */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) + goto out; + client_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!client_id) + goto out; - out: - silc_id_payload_free(idp); -} + /* If the the client is not in local list we check global list */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!client) { + client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, NULL); + if (!client) { + silc_free(client_id); + goto out; + } + } -/* Receoved New Id List packet, list of New ID payloads inside one - packet. Process the New ID payloads one by one. */ + /* Send to channel */ + silc_server_packet_send_to_channel(server, sock, channel, packet->type, + FALSE, packet->buffer->data, + packet->buffer->len, FALSE); -void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcPacketContext *new_id; - SilcBuffer idp; - unsigned short id_len; + /* Remove the client from channel */ + silc_server_remove_from_one_channel(server, sock, channel, client, FALSE); - SILC_LOG_DEBUG(("Processing New ID List")); + break; - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type != SILC_ID_SERVER) - return; + case SILC_NOTIFY_TYPE_KILLED: + { + /* + * Distribute the notify to local clients on channels + */ + unsigned char *id; + uint32 id_len; + + SILC_LOG_DEBUG(("KILLED notify")); + + /* Get client ID */ + id = silc_argument_get_arg_type(args, 1, &id_len); + if (!id) + goto out; + client_id = silc_id_payload_parse_id(id, id_len); + if (!client_id) + goto out; - /* 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; + /* If the the client is not in local list we check global list */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!client) { + client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, NULL); + if (!client) { + silc_free(client_id); + goto out; + } + } + silc_free(client_id); - idp = silc_buffer_alloc(256); - new_id->buffer = idp; + /* If the client is one of ours, then close the connection to the + client now. This removes the client from all channels as well. */ + if (packet->dst_id_type == SILC_ID_CLIENT && client->connection) { + sock = client->connection; + silc_server_free_client_data(server, NULL, client, FALSE, NULL); + silc_server_close_connection(server, sock); + break; + } - while (packet->buffer->len) { - SILC_GET16_MSB(id_len, packet->buffer->data + 2); - if ((id_len > packet->buffer->len) || - (id_len > idp->truelen)) - break; + /* Get comment */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (tmp_len > 128) + tmp = NULL; - silc_buffer_pull_tail(idp, 4 + id_len); - silc_buffer_put(idp, packet->buffer->data, 4 + id_len); + /* Send the notify to local clients on the channels except to the + client who is killed. */ + silc_server_send_notify_on_channels(server, client, client, + SILC_NOTIFY_TYPE_KILLED, + tmp ? 2 : 1, + id, id_len, + tmp, tmp_len); - /* Process the New ID */ - silc_server_new_id(server, sock, new_id); + /* Remove the client from all channels */ + silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, + FALSE); - silc_buffer_push_tail(idp, 4 + id_len); - silc_buffer_pull(packet->buffer, 4 + id_len); - } + break; + } - silc_buffer_free(idp); - silc_free(new_id); -} + case SILC_NOTIFY_TYPE_UMODE_CHANGE: + /* + * Save the mode of the client. + */ -/* 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. */ + SILC_LOG_DEBUG(("UMODE_CHANGE notify")); -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; + /* Get client ID */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) + goto out; + client_id = silc_id_payload_parse_id(tmp, tmp_len); + if (!client_id) + goto out; - SILC_LOG_DEBUG(("Processing New Channel")); + /* Get client entry */ + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!client) { + client = silc_idlist_find_client_by_id(server->local_list, + client_id, TRUE, NULL); + if (!client) { + silc_free(client_id); + goto out; + } + } + silc_free(client_id); - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type != SILC_ID_SERVER || - server->server_type == SILC_SERVER) - return; + /* Get the mode */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (!tmp) + goto out; + SILC_GET32_MSB(mode, tmp); + +#define SILC_UMODE_STATS_UPDATE(oper, mod) \ +do { \ + if (client->mode & (mod)) { \ + if (!(mode & (mod))) { \ + if (client->connection) \ + server->stat.my_ ## oper ## _ops--; \ + if (server->server_type == SILC_ROUTER) \ + server->stat. oper ## _ops--; \ + } \ + } else { \ + if (mode & (mod)) { \ + if (client->connection) \ + server->stat.my_ ## oper ## _ops++; \ + if (server->server_type == SILC_ROUTER) \ + server->stat. oper ## _ops++; \ + } \ + } \ +} while(0) + + /* Update statistics */ + SILC_UMODE_STATS_UPDATE(server, SILC_UMODE_SERVER_OPERATOR); + SILC_UMODE_STATS_UPDATE(router, SILC_UMODE_ROUTER_OPERATOR); + + /* Save the mode */ + client->mode = mode; - /* 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; + break; - 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. */ + case SILC_NOTIFY_TYPE_BAN: + /* + * Save the ban + */ - SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s", - silc_id_render(channel_id, SILC_ID_CHANNEL), - sock->hostname)); + SILC_LOG_DEBUG(("BAN notify")); - 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)); + /* Get Channel 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; - /* 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. */ + /* Get channel entry */ + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); 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); + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_free(channel_id); + goto out; } + } + silc_free(channel_id); - /* 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); + /* Get the new ban and add it to the ban list */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (tmp) { + if (!channel->ban_list) + channel->ban_list = silc_calloc(tmp_len + 2, + sizeof(*channel->ban_list)); + else + channel->ban_list = silc_realloc(channel->ban_list, + sizeof(*channel->ban_list) * + (tmp_len + + strlen(channel->ban_list) + 2)); + strncat(channel->ban_list, tmp, tmp_len); + strncat(channel->ban_list, ",", 1); + } - /* 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); + /* Get the ban to be removed and remove it from the list */ + tmp = silc_argument_get_arg_type(args, 3, &tmp_len); + if (tmp && channel->ban_list) { + char *start, *end, *n; + + if (!strncmp(channel->ban_list, tmp, strlen(channel->ban_list) - 1)) { + silc_free(channel->ban_list); + channel->ban_list = NULL; + } else { + start = strstr(channel->ban_list, tmp); + if (start && strlen(start) >= tmp_len) { + end = start + tmp_len; + n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n)); + strncat(n, channel->ban_list, start - channel->ban_list); + strncat(n, end + 1, ((channel->ban_list + + strlen(channel->ban_list)) - end) - 1); + silc_free(channel->ban_list); + channel->ban_list = n; + } + } } + break; + + /* Ignore rest of the notify types for now */ + case SILC_NOTIFY_TYPE_NONE: + case SILC_NOTIFY_TYPE_MOTD: + break; + default: + break; } - silc_free(id); + out: + silc_notify_payload_free(payload); } -/* 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) +void silc_server_notify_list(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { SilcPacketContext *new; SilcBuffer buffer; - unsigned short len1, len2; + uint16 len; - SILC_LOG_DEBUG(("Processing New Channel List")); + SILC_LOG_DEBUG(("Processing Notify List")); if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type != SILC_ID_SERVER || - server->server_type == SILC_SERVER) + 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 = silc_packet_context_alloc(); - new->type = SILC_PACKET_NEW_CHANNEL; + new->type = SILC_PACKET_NOTIFY; new->flags = packet->flags; new->src_id = packet->src_id; new->src_id_len = packet->src_id_len; @@ -991,839 +1213,1393 @@ void silc_server_new_channel_list(SilcServer server, new->dst_id_len = packet->dst_id_len; new->dst_id_type = packet->dst_id_type; - buffer = silc_buffer_alloc(512); + buffer = silc_buffer_alloc(1024); new->buffer = buffer; while (packet->buffer->len) { - SILC_GET16_MSB(len1, packet->buffer->data); - if ((len1 > packet->buffer->len) || - (len1 > buffer->truelen)) + SILC_GET16_MSB(len, packet->buffer->data + 2); + if (len > packet->buffer->len) break; - SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1); - if ((len2 > packet->buffer->len) || - (len2 > buffer->truelen)) - break; + if (len > buffer->truelen) { + silc_buffer_free(buffer); + buffer = silc_buffer_alloc(1024 + len); + } - silc_buffer_pull_tail(buffer, 4 + len1 + len2); - silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2); + silc_buffer_pull_tail(buffer, len); + silc_buffer_put(buffer, packet->buffer->data, len); - /* Process the New Channel */ - silc_server_new_channel(server, sock, new); + /* Process the Notify */ + silc_server_notify(server, sock, new); - silc_buffer_push_tail(buffer, 4 + len1 + len2); - silc_buffer_pull(packet->buffer, 4 + len1 + len2); + silc_buffer_push_tail(buffer, len); + silc_buffer_pull(packet->buffer, len); } 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 - receive it. */ +/* Received private message. This resolves the destination of the message + and sends the packet. This is used by both server and router. If the + destination is our locally connected client this sends the packet to + the client. This may also send the message for further routing if + the destination is not in our server (or router). */ -void silc_server_remove_channel_user(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_server_private_message(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { - SilcBuffer buffer = packet->buffer; - unsigned char *tmp1 = NULL, *tmp2 = NULL; - unsigned short tmp1_len, tmp2_len; - SilcClientID *client_id = NULL; - SilcChannelID *channel_id = NULL; - SilcChannelEntry channel; - SilcClientEntry client; - int ret; + SilcSocketConnection dst_sock; + SilcIDListData idata; - SILC_LOG_DEBUG(("Removing user from channel")); + SILC_LOG_DEBUG(("Start")); - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type != SILC_ID_SERVER || - server->server_type == SILC_SERVER) + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT || !packet->dst_id) return; - ret = silc_buffer_unformat(buffer, - SILC_STR_UI16_NSTRING_ALLOC(&tmp1, &tmp1_len), - SILC_STR_UI16_NSTRING_ALLOC(&tmp2, &tmp2_len), - SILC_STR_END); - if (ret == -1) - goto out; - - client_id = silc_id_str2id(tmp1, tmp1_len, SILC_ID_CLIENT); - channel_id = silc_id_str2id(tmp2, tmp2_len, SILC_ID_CHANNEL); - if (!client_id || !channel_id) - goto out; - - /* If we are router and this packet is not already broadcast packet - we will broadcast it. The sending socket really cannot be router or - the router is buggy. If this packet is coming from router then it must - have the broadcast flag set already and we won't do anything. */ - if (!server->standalone && server->server_type == SILC_ROUTER && - sock->type == SILC_SOCKET_TYPE_SERVER && - !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received Remove Channel User packet")); - silc_server_packet_send(server, server->router->connection, packet->type, - packet->flags | SILC_PACKET_FLAG_BROADCAST, - buffer->data, buffer->len, FALSE); - } + /* Get the route to the client */ + dst_sock = silc_server_get_client_route(server, packet->dst_id, + packet->dst_id_len, NULL, &idata); + if (!dst_sock) { + /* Send IDENTIFY command reply with error status to indicate that + such destination ID does not exist or is invalid */ + SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id, + packet->dst_id_len, + packet->dst_id_type); + if (!idp) + return; - /* Get channel entry */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); - if (!channel) { - channel = silc_idlist_find_channel_by_id(server->global_list, - channel_id, NULL); - if (!channel) - goto out; - } + if (packet->src_id_type == SILC_ID_CLIENT) { + SilcClientID *client_id = silc_id_str2id(packet->src_id, + packet->src_id_len, + packet->src_id_type); + silc_server_send_dest_command_reply(server, sock, + client_id, SILC_ID_CLIENT, + SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, + 0, 1, 2, idp->data, idp->len); + silc_free(client_id); + } else { + silc_server_send_command_reply(server, sock, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, + 0, 1, 2, idp->data, idp->len); + } - /* Get client entry */ - client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL); - if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); - if (!client) - goto out; + silc_buffer_free(idp); + return; } - /* Remove user from channel */ - silc_server_remove_from_one_channel(server, sock, channel, client, TRUE); - - out: - if (tmp1) - silc_free(tmp1); - if (tmp2) - silc_free(tmp2); - if (client_id) - silc_free(client_id); - if (channel_id) - silc_free(channel_id); + /* Send the private message */ + silc_server_send_private_message(server, dst_sock, idata->send_key, + idata->hmac_send, idata->psn_send++, + packet); } -/* Received New Channel User List packet, list of New Channel User payloads - inside one packet. Process the payloads one by one. */ +/* Received private message key packet.. This packet is never for us. It is to + the client in the packet's destination ID. Sending of this sort of packet + equals sending private message, ie. it is sent point to point from + one client to another. */ -void silc_server_new_channel_user_list(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_server_private_message_key(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { - SilcPacketContext *new; - SilcBuffer buffer; - unsigned short len1, len2; + SilcSocketConnection dst_sock; + SilcIDListData idata; - SILC_LOG_DEBUG(("Processing New Channel User List")); + SILC_LOG_DEBUG(("Start")); - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type != SILC_ID_SERVER || - server->server_type == SILC_SERVER) + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT) 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_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_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 User */ - silc_server_new_channel_user(server, sock, new); + if (!packet->dst_id) + return; - silc_buffer_push_tail(buffer, 4 + len1 + len2); - silc_buffer_pull(packet->buffer, 4 + len1 + len2); - } + /* Get the route to the client */ + dst_sock = silc_server_get_client_route(server, packet->dst_id, + packet->dst_id_len, NULL, &idata); + if (!dst_sock) + return; - silc_buffer_free(buffer); - silc_free(new); + /* Relay the packet */ + silc_server_relay_packet(server, dst_sock, idata->send_key, + idata->hmac_send, idata->psn_send++, packet, FALSE); } -/* Received notify packet. Server can receive notify packets from router. - Server then relays the notify messages to clients if needed. */ +/* Processes incoming command reply packet. The command reply packet may + be destined to one of our clients or it may directly for us. We will + call the command reply routine after processing the packet. */ -void silc_server_notify(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_server_command_reply(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { - SilcNotifyPayload payload; - SilcNotifyType type; - SilcArgumentPayload args; - SilcChannelID *channel_id; - SilcClientID *client_id, *client_id2; - SilcChannelEntry channel; - SilcClientEntry client; - unsigned char *tmp; - unsigned int tmp_len; + SilcBuffer buffer = packet->buffer; + SilcClientEntry client = NULL; + SilcSocketConnection dst_sock; + SilcIDListData idata; + SilcClientID *id = NULL; SILC_LOG_DEBUG(("Start")); - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type != SILC_ID_SERVER) - return; - - /* XXX: For now we expect that the we are normal server and that the - sender is router. Server could send (protocol allows it) notify to - router but we don't support it yet. */ - if (server->server_type != SILC_SERVER && + /* Source must be server or router */ + if (packet->src_id_type != SILC_ID_SERVER && sock->type != SILC_SOCKET_TYPE_ROUTER) return; - payload = silc_notify_payload_parse(packet->buffer); - if (!payload) + if (packet->dst_id_type == SILC_ID_CHANNEL) return; - type = silc_notify_get_type(payload); - args = silc_notify_get_args(payload); - if (!args) - goto out; + if (packet->dst_id_type == SILC_ID_CLIENT) { + /* Destination must be one of ours */ + id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT); + if (!id) + return; + client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL); + if (!client) { + SILC_LOG_ERROR(("Cannot process command reply to unknown client")); + silc_free(id); + return; + } + } - switch(type) { - case SILC_NOTIFY_TYPE_JOIN: - /* - * Distribute the notify to local clients on the channel - */ - SILC_LOG_DEBUG(("JOIN notify")); + if (packet->dst_id_type == SILC_ID_SERVER) { + /* For now this must be for us */ + if (memcmp(packet->dst_id, server->id_string, server->id_string_len)) { + SILC_LOG_ERROR(("Cannot process command reply to unknown server")); + return; + } + } - channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, - packet->dst_id_type); - if (!channel_id) - goto out; + /* Execute command reply locally for the command */ + silc_server_command_reply_process(server, sock, buffer); - /* Get channel entry */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); - if (!channel) { - silc_free(channel_id); - goto out; - } + if (packet->dst_id_type == SILC_ID_CLIENT && client && id) { + /* Relay the packet to the client */ + + dst_sock = (SilcSocketConnection)client->connection; + silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + + silc_packet_send_prepare(dst_sock, 0, 0, buffer->len); + silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len); + + idata = (SilcIDListData)client; + + /* Encrypt packet */ + silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++, + dst_sock->outbuf, buffer->len); + + /* Send the packet */ + silc_server_packet_send_real(server, dst_sock, TRUE); - /* Get client ID */ - tmp = silc_argument_get_arg_type(args, 1, &tmp_len); - if (!tmp) { - silc_free(channel_id); - goto out; - } - client_id = silc_id_payload_parse_id(tmp, tmp_len); - if (!client_id) { - silc_free(channel_id); - goto out; - } + silc_free(id); + } +} - /* Send to channel */ - silc_server_packet_send_to_channel(server, NULL, channel, packet->type, - FALSE, packet->buffer->data, - packet->buffer->len, FALSE); +/* Process received channel message. The message can be originated from + client or server. */ - /* If the the client is not in local list we check global list (ie. the - channel will be global channel) and if it does not exist then create - entry for the client. */ - client = silc_idlist_find_client_by_id(server->local_list, - client_id, NULL); - if (!client) { - SilcChannelClientEntry chl; +void silc_server_channel_message(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcChannelEntry channel = NULL; + SilcChannelID *id = NULL; + void *sender = NULL; + void *sender_entry = NULL; + bool local = TRUE; - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); - if (!client) { - client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL, - client_id, sock->user_data, NULL); - if (!client) { - silc_free(channel_id); - silc_free(client_id); - goto out; - } - } + SILC_LOG_DEBUG(("Processing channel message")); - /* The channel is global now */ - channel->global_users = TRUE; + /* Sanity checks */ + if (packet->dst_id_type != SILC_ID_CHANNEL) { + SILC_LOG_DEBUG(("Received bad message for channel, dropped")); + goto out; + } - /* Now actually JOIN the global client to the channel */ - chl = silc_calloc(1, sizeof(*chl)); - chl->client = client; - chl->channel = channel; - silc_list_add(channel->user_list, chl); - silc_list_add(client->channels, chl); - } else { - silc_free(client_id); + /* Find channel entry */ + id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL); + if (!id) + goto out; + channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL); + if (!channel) { + channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL); + if (!channel) { + SILC_LOG_DEBUG(("Could not find channel")); + goto out; } - break; - - case SILC_NOTIFY_TYPE_LEAVE: - /* - * Distribute the notify to local clients on the channel - */ - SILC_LOG_DEBUG(("LEAVE notify")); + } - channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, - packet->dst_id_type); - if (!channel_id) + /* See that this client is on the channel. If the original sender is + not client (as it can be server as well) we don't do the check. */ + sender = silc_id_str2id(packet->src_id, packet->src_id_len, + packet->src_id_type); + if (!sender) + goto out; + if (packet->src_id_type == SILC_ID_CLIENT) { + sender_entry = silc_idlist_find_client_by_id(server->local_list, + sender, TRUE, NULL); + if (!sender_entry) { + local = FALSE; + sender_entry = silc_idlist_find_client_by_id(server->global_list, + sender, TRUE, NULL); + } + if (!sender_entry || !silc_server_client_on_channel(sender_entry, + channel)) { + SILC_LOG_DEBUG(("Client not on channel")); goto out; + } - /* Get channel entry */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); - if (!channel) { - silc_free(channel_id); + /* If the packet is coming from router, but the client entry is + local entry to us then some router is rerouting this to us and it is + not allowed. */ + if (server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_ROUTER && local) { + SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it")); goto out; } + } - /* Get client ID */ - tmp = silc_argument_get_arg_type(args, 1, &tmp_len); - if (!tmp) { - silc_free(channel_id); - goto out; + /* Distribute the packet to our local clients. This will send the + packet for further routing as well, if needed. */ + silc_server_packet_relay_to_channel(server, sock, channel, sender, + packet->src_id_type, sender_entry, + packet->buffer->data, + packet->buffer->len, FALSE); + + out: + if (sender) + silc_free(sender); + if (id) + silc_free(id); +} + +/* Received channel key packet. We distribute the key to all of our locally + connected clients on the channel. */ + +void silc_server_channel_key(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcChannelEntry channel; + + if (packet->src_id_type != SILC_ID_SERVER || + (server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_ROUTER)) + return; + + /* Save the channel key */ + channel = silc_server_save_channel_key(server, buffer, NULL); + if (!channel) + return; + + /* Distribute the key to everybody who is on the channel. If we are router + we will also send it to locally connected servers. */ + silc_server_send_channel_key(server, sock, channel, FALSE); + + if (server->server_type != SILC_BACKUP_ROUTER) { + /* Distribute to local cell backup routers. */ + silc_server_backup_send(server, (SilcServerEntry)sock->user_data, + SILC_PACKET_CHANNEL_KEY, 0, + buffer->data, buffer->len, FALSE, TRUE); + } +} + +/* Received New Client packet and processes it. Creates Client ID for the + client. Client becomes registered after calling this functions. */ + +SilcClientEntry silc_server_new_client(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcClientEntry client; + SilcClientID *client_id; + SilcBuffer reply; + SilcIDListData idata; + char *username = NULL, *realname = NULL, *id_string; + uint16 username_len; + uint32 id_len; + int ret; + char *hostname, *nickname; + int nickfail = 0; + + SILC_LOG_DEBUG(("Creating new client")); + + if (sock->type != SILC_SOCKET_TYPE_CLIENT) + return NULL; + + /* Take client entry */ + client = (SilcClientEntry)sock->user_data; + idata = (SilcIDListData)client; + + /* Remove the old cache entry. */ + if (!silc_idcache_del_by_context(server->local_list->clients, client)) { + SILC_LOG_ERROR(("Lost client's cache entry - bad thing")); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Unknown client"); + return NULL; + } + + /* Parse incoming packet */ + ret = silc_buffer_unformat(buffer, + SILC_STR_UI16_NSTRING_ALLOC(&username, + &username_len), + SILC_STR_UI16_STRING_ALLOC(&realname), + SILC_STR_END); + if (ret == -1) { + silc_free(username); + silc_free(realname); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Incomplete client information"); + return NULL; + } + + if (!username) { + silc_free(username); + silc_free(realname); + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Incomplete client information"); + return NULL; + } + + if (username_len > 128) + username[128] = '\0'; + + /* Check for bad characters for nickname, and modify the nickname if + it includes those. */ + if (silc_server_name_bad_chars(username, username_len)) { + nickname = silc_server_name_modify_bad(username, username_len); + } else { + nickname = strdup(username); + } + + /* Make sanity checks for the hostname of the client. If the hostname + is provided in the `username' check that it is the same than the + resolved hostname, or if not resolved the hostname that appears in + the client's public key. If the hostname is not present then put + it from the resolved name or from the public key. */ + if (strchr(username, '@')) { + SilcPublicKeyIdentifier pident; + int tlen = strcspn(username, "@"); + char *phostname = NULL; + + hostname = silc_memdup(username + tlen + 1, strlen(username) - tlen - 1); + + if (strcmp(sock->hostname, sock->ip) && + strcmp(sock->hostname, hostname)) { + silc_free(username); + silc_free(hostname); + silc_free(realname); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Incomplete client information"); + return NULL; } - client_id = silc_id_payload_parse_id(tmp, tmp_len); - if (!client_id) { - silc_free(channel_id); - goto out; + + pident = silc_pkcs_decode_identifier(client->data.public_key->identifier); + if (pident) { + phostname = strdup(pident->host); + silc_pkcs_free_identifier(pident); } - /* Send to channel */ - silc_server_packet_send_to_channel(server, NULL, channel, packet->type, - FALSE, packet->buffer->data, - packet->buffer->len, FALSE); - - /* Get client entry */ - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); - if (!client) { - client = silc_idlist_find_client_by_id(server->local_list, - client_id, NULL); - if (!client) { - silc_free(client_id); - silc_free(channel_id); - goto out; + if (!strcmp(sock->hostname, sock->ip) && + phostname && strcmp(phostname, hostname)) { + silc_free(username); + silc_free(hostname); + silc_free(phostname); + silc_free(realname); + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Incomplete client information"); + return NULL; + } + + silc_free(phostname); + } else { + /* The hostname is not present, add it. */ + char *newusername; + /* XXX For now we cannot take the host name from the public key since + they are not trusted or we cannot verify them as trusted. Just take + what the resolved name or address is. */ +#if 0 + if (strcmp(sock->hostname, sock->ip)) { +#endif + newusername = silc_calloc(strlen(username) + + strlen(sock->hostname) + 2, + sizeof(*newusername)); + strncat(newusername, username, strlen(username)); + strncat(newusername, "@", 1); + strncat(newusername, sock->hostname, strlen(sock->hostname)); + silc_free(username); + username = newusername; +#if 0 + } else { + SilcPublicKeyIdentifier pident = + silc_pkcs_decode_identifier(client->data.public_key->identifier); + + if (pident) { + newusername = silc_calloc(strlen(username) + + strlen(pident->host) + 2, + sizeof(*newusername)); + strncat(newusername, username, strlen(username)); + strncat(newusername, "@", 1); + strncat(newusername, pident->host, strlen(pident->host)); + silc_free(username); + username = newusername; + silc_pkcs_free_identifier(pident); } } - silc_free(client_id); +#endif + } - /* Remove the user from channel */ - silc_server_remove_from_one_channel(server, sock, channel, client, FALSE); - break; + /* Create Client ID */ + while (!silc_id_create_client_id(server, server->id, server->rng, + server->md5hash, nickname, &client_id)) { + nickfail++; + snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail); + } + + /* Update client entry */ + idata->status |= SILC_IDLIST_STATUS_REGISTERED; + client->nickname = nickname; + client->username = username; + client->userinfo = realname ? realname : strdup(" "); + client->id = client_id; + id_len = silc_id_get_len(client_id, SILC_ID_CLIENT); + + /* Add the client again to the ID cache */ + silc_idcache_add(server->local_list->clients, client->nickname, + client_id, client, 0, NULL); + + /* Notify our router about new client on the SILC network */ + if (!server->standalone) + silc_server_send_new_id(server, (SilcSocketConnection) + server->router->connection, + server->server_type == SILC_ROUTER ? TRUE : FALSE, + client->id, SILC_ID_CLIENT, id_len); + + /* Send the new client ID to the client. */ + id_string = silc_id_id2str(client->id, SILC_ID_CLIENT); + reply = silc_buffer_alloc(2 + 2 + id_len); + silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply)); + silc_buffer_format(reply, + SILC_STR_UI_SHORT(SILC_ID_CLIENT), + SILC_STR_UI_SHORT(id_len), + SILC_STR_UI_XNSTRING(id_string, id_len), + SILC_STR_END); + silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, + reply->data, reply->len, FALSE); + silc_free(id_string); + silc_buffer_free(reply); + + /* Send some nice info to the client */ + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Welcome to the SILC Network %s", + username)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Your host is %s, running version %s", + server->config->server_info->server_name, + server_version)); + if (server->server_type == SILC_ROUTER) { + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("There are %d clients on %d servers in SILC " + "Network", server->stat.clients, + server->stat.servers + 1)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("There are %d clients on %d server in our cell", + server->stat.cell_clients, + server->stat.cell_servers + 1)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("I have %d clients, %d channels, %d servers and " + "%d routers", + server->stat.my_clients, + server->stat.my_channels, + server->stat.my_servers, + server->stat.my_routers)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("There are %d server operators and %d router " + "operators online", + server->stat.server_ops, + server->stat.router_ops)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("I have %d operators online", + server->stat.my_router_ops + + server->stat.my_server_ops)); + } else { + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("I have %d clients and %d channels formed", + server->stat.my_clients, + server->stat.my_channels)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("%d operators online", + server->stat.my_server_ops)); + } + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Your connection is secured with %s cipher, " + "key length %d bits", + idata->send_key->cipher->name, + idata->send_key->cipher->key_len)); + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("Your current nickname is %s", + client->nickname)); + + /* Send motd */ + silc_server_send_motd(server, sock); + + return client; +} + +/* Create new server. This processes received New Server packet and + saves the received Server ID. The server is our locally connected + server thus we save all the information and save it to local list. + This funtion can be used by both normal server and router server. + If normal server uses this it means that its router has connected + to the server. If router uses this it means that one of the cell's + servers is connected to the router. */ + +SilcServerEntry silc_server_new_server(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcServerEntry new_server, server_entry; + SilcServerID *server_id; + SilcIDListData idata; + unsigned char *server_name, *id_string; + uint16 id_len, name_len; + int ret; + bool local = TRUE; + + SILC_LOG_DEBUG(("Creating new server")); + + if (sock->type != SILC_SOCKET_TYPE_SERVER && + sock->type != SILC_SOCKET_TYPE_ROUTER) + return NULL; + + /* Take server entry */ + new_server = (SilcServerEntry)sock->user_data; + idata = (SilcIDListData)new_server; + + /* Remove the old cache entry */ + if (!silc_idcache_del_by_context(server->local_list->servers, new_server)) { + silc_idcache_del_by_context(server->global_list->servers, new_server); + local = FALSE; + } + + /* Parse the incoming packet */ + ret = silc_buffer_unformat(buffer, + SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len), + SILC_STR_UI16_NSTRING_ALLOC(&server_name, + &name_len), + SILC_STR_END); + if (ret == -1) { + if (id_string) + silc_free(id_string); + if (server_name) + silc_free(server_name); + return NULL; + } + + if (id_len > buffer->len) { + silc_free(id_string); + silc_free(server_name); + return NULL; + } + + if (name_len > 256) + server_name[255] = '\0'; + + /* Get Server ID */ + server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER); + if (!server_id) { + silc_free(id_string); + silc_free(server_name); + return NULL; + } + silc_free(id_string); + + /* Check that we do not have this ID already */ + server_entry = silc_idlist_find_server_by_id(server->local_list, + server_id, TRUE, NULL); + if (server_entry) { + silc_idcache_del_by_context(server->local_list->servers, server_entry); + } else { + server_entry = silc_idlist_find_server_by_id(server->global_list, + server_id, TRUE, NULL); + if (server_entry) + silc_idcache_del_by_context(server->global_list->servers, server_entry); + } + + /* Update server entry */ + idata->status |= SILC_IDLIST_STATUS_REGISTERED; + new_server->server_name = server_name; + new_server->id = server_id; + + SILC_LOG_DEBUG(("New server id(%s)", + silc_id_render(server_id, SILC_ID_SERVER))); + + /* Add again the entry to the ID cache. */ + silc_idcache_add(local ? server->local_list->servers : + server->global_list->servers, server_name, server_id, + new_server, 0, NULL); + + /* Distribute the information about new server in the SILC network + to our router. If we are normal server we won't send anything + since this connection must be our router connection. */ + if (server->server_type == SILC_ROUTER && !server->standalone && + server->router->connection != sock) + silc_server_send_new_id(server, server->router->connection, + TRUE, new_server->id, SILC_ID_SERVER, + silc_id_get_len(server_id, SILC_ID_SERVER)); + + if (server->server_type == SILC_ROUTER) + server->stat.cell_servers++; + + /* Check whether this router connection has been replaced by an + backup router. If it has been then we'll disable the server and will + ignore everything it will send until the backup router resuming + protocol has been completed. */ + if (sock->type == SILC_SOCKET_TYPE_ROUTER && + silc_server_backup_replaced_get(server, server_id, NULL)) { + /* Send packet to the server indicating that it cannot use this + connection as it has been replaced by backup router. */ + SilcBuffer packet = silc_buffer_alloc(2); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_REPLACED), + SILC_STR_UI_CHAR(0), + SILC_STR_END); + silc_server_packet_send(server, sock, + SILC_PACKET_RESUME_ROUTER, 0, + packet->data, packet->len, TRUE); + silc_buffer_free(packet); + + /* Mark the router disabled. The data sent earlier will go but nothing + after this does not go to this connection. */ + idata->status |= SILC_IDLIST_STATUS_DISABLED; + } else { + /* If it is router announce our stuff to it. */ + if (sock->type == SILC_SOCKET_TYPE_ROUTER && + server->server_type == SILC_ROUTER) { + silc_server_announce_servers(server, FALSE, 0, sock); + silc_server_announce_clients(server, 0, sock); + silc_server_announce_channels(server, 0, sock); + } + } + + return new_server; +} + +/* Processes incoming New ID packet. New ID Payload is used to distribute + information about newly registered clients and servers. */ + +static void silc_server_new_id_real(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet, + int broadcast) +{ + SilcBuffer buffer = packet->buffer; + SilcIDList id_list; + SilcServerEntry router, server_entry; + SilcSocketConnection router_sock; + SilcIDPayload idp; + SilcIdType id_type; + void *id; + + SILC_LOG_DEBUG(("Processing new ID")); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + server->server_type == SILC_SERVER || + packet->src_id_type != SILC_ID_SERVER) + return; + + idp = silc_id_payload_parse(buffer->data, buffer->len); + if (!idp) + return; + + id_type = silc_id_payload_get_type(idp); + + /* Normal server cannot have other normal server connections */ + server_entry = (SilcServerEntry)sock->user_data; + if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER && + server_entry->server_type == SILC_SERVER) + goto out; + + id = silc_id_payload_get_id(idp); + if (!id) + goto out; + + /* If the packet is coming from server then use the sender as the + origin of the the packet. If it came from router then check the real + sender of the packet and use that as the origin. */ + if (sock->type == SILC_SOCKET_TYPE_SERVER) { + id_list = server->local_list; + router_sock = sock; + router = sock->user_data; + + /* If the sender is backup router and ID is server (and we are not + backup router) then switch the entry to global list. */ + if (server_entry->server_type == SILC_BACKUP_ROUTER && + id_type == SILC_ID_SERVER && + server->id_entry->server_type != SILC_BACKUP_ROUTER) { + id_list = server->global_list; + router_sock = server->router ? server->router->connection : sock; + } + } else { + void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len, + packet->src_id_type); + router = silc_idlist_find_server_by_id(server->global_list, + sender_id, TRUE, NULL); + if (!router) + router = silc_idlist_find_server_by_id(server->local_list, + sender_id, TRUE, NULL); + silc_free(sender_id); + router_sock = sock; + id_list = server->global_list; + } - case SILC_NOTIFY_TYPE_SIGNOFF: - /* - * Distribute the notify to local clients on the channel - */ - SILC_LOG_DEBUG(("SIGNOFF notify")); + if (!router) + goto out; - /* Get client ID */ - tmp = silc_argument_get_arg_type(args, 1, &tmp_len); - if (!tmp) - goto out; - client_id = silc_id_payload_parse_id(tmp, tmp_len); - if (!client_id) - goto out; + switch(id_type) { + case SILC_ID_CLIENT: + { + SilcClientEntry entry; - /* Get client entry */ - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); - if (!client) { - client = silc_idlist_find_client_by_id(server->local_list, - client_id, NULL); - if (!client) { - silc_free(client_id); + /* Check that we do not have this client already */ + entry = silc_idlist_find_client_by_id(server->global_list, + id, server->server_type, + NULL); + if (!entry) + entry = silc_idlist_find_client_by_id(server->local_list, + id, server->server_type, + NULL); + if (entry) { + SILC_LOG_DEBUG(("Ignoring client that we already have")); goto out; } - } - silc_free(client_id); - /* Remove the client from all channels */ - silc_server_remove_from_channels(server, NULL, client); + SILC_LOG_DEBUG(("New client id(%s) from [%s] %s", + silc_id_render(id, SILC_ID_CLIENT), + sock->type == SILC_SOCKET_TYPE_SERVER ? + "Server" : "Router", sock->hostname)); + + /* As a router we keep information of all global information in our + global list. Cell wide information however is kept in the local + list. */ + entry = silc_idlist_add_client(id_list, NULL, NULL, NULL, + id, router, NULL, 0); + if (!entry) { + SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + + /* Inform the sender that the ID is not usable */ + silc_server_send_notify_signoff(server, sock, FALSE, id, NULL); + goto out; + } + entry->nickname = NULL; + entry->data.status |= SILC_IDLIST_STATUS_REGISTERED; - /* Remove the client entry */ - silc_idlist_del_client(server->global_list, client); + if (sock->type == SILC_SOCKET_TYPE_SERVER) + server->stat.cell_clients++; + server->stat.clients++; + } break; - case SILC_NOTIFY_TYPE_NICK_CHANGE: + case SILC_ID_SERVER: { - /* - * Distribute the notify to local clients on the channel - */ - unsigned char *id, *id2; + SilcServerEntry entry; - SILC_LOG_DEBUG(("NICK CHANGE notify")); + /* If the ID is mine, ignore it. */ + if (SILC_ID_SERVER_COMPARE(id, server->id)) { + SILC_LOG_DEBUG(("Ignoring my own ID as new ID")); + break; + } + + /* If the ID is the sender's ID, ignore it (we have it already) */ + if (SILC_ID_SERVER_COMPARE(id, router->id)) { + SILC_LOG_DEBUG(("Ignoring sender's own ID")); + break; + } - /* Get old client ID */ - id = silc_argument_get_arg_type(args, 1, &tmp_len); - if (!id) - goto out; - client_id = silc_id_payload_parse_id(id, tmp_len); - if (!client_id) + /* Check that we do not have this server already */ + entry = silc_idlist_find_server_by_id(server->global_list, + id, server->server_type, + NULL); + if (!entry) + entry = silc_idlist_find_server_by_id(server->local_list, + id, server->server_type, + NULL); + if (entry) { + SILC_LOG_DEBUG(("Ignoring server that we already have")); goto out; + } + + SILC_LOG_DEBUG(("New server id(%s) from [%s] %s", + silc_id_render(id, SILC_ID_SERVER), + sock->type == SILC_SOCKET_TYPE_SERVER ? + "Server" : "Router", sock->hostname)); - /* Get new client ID */ - id2 = silc_argument_get_arg_type(args, 2, &tmp_len); - if (!id2) - goto out; - client_id2 = silc_id_payload_parse_id(id2, tmp_len); - if (!client_id2) + /* As a router we keep information of all global information in our + global list. Cell wide information however is kept in the local + list. */ + entry = silc_idlist_add_server(id_list, NULL, 0, id, router, + router_sock); + if (!entry) { + SILC_LOG_ERROR(("Could not add new server to the ID Cache")); goto out; + } + entry->data.status |= SILC_IDLIST_STATUS_REGISTERED; - SILC_LOG_DEBUG(("Old Client ID id(%s)", - silc_id_render(client_id, SILC_ID_CLIENT))); - SILC_LOG_DEBUG(("New Client ID id(%s)", - silc_id_render(client_id2, SILC_ID_CLIENT))); - - /* Replace the Client ID */ - client = silc_idlist_replace_client_id(server->global_list, client_id, - client_id2); - if (!client) - client = silc_idlist_replace_client_id(server->local_list, client_id, - client_id2); - - if (client) - /* Send the NICK_CHANGE notify type to local clients on the channels - this client is joined to. */ - silc_server_send_notify_on_channels(server, client, - SILC_NOTIFY_TYPE_NICK_CHANGE, 2, - id, tmp_len, - id2, tmp_len); - - silc_free(client_id); - if (!client) - silc_free(client_id2); - break; + if (sock->type == SILC_SOCKET_TYPE_SERVER) + server->stat.cell_servers++; + server->stat.servers++; } - case SILC_NOTIFY_TYPE_TOPIC_SET: - /* - * Distribute the notify to local clients on the channel - */ - SILC_LOG_DEBUG(("TOPIC SET notify (not-impl XXX)")); - break; - - case SILC_NOTIFY_TYPE_CMODE_CHANGE: - /* - * Distribute the notify to local clients on the channel - */ - SILC_LOG_DEBUG(("CMODE CHANGE notify (not-impl XXX)")); break; - case SILC_NOTIFY_TYPE_CUMODE_CHANGE: - /* - * Distribute the notify to local clients on the channel - */ - SILC_LOG_DEBUG(("CUMODE CHANGE notify (not-impl XXX)")); + case SILC_ID_CHANNEL: + SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet")); + goto out; break; - /* Ignore rest notify types for now */ - case SILC_NOTIFY_TYPE_NONE: - case SILC_NOTIFY_TYPE_INVITE: - case SILC_NOTIFY_TYPE_MOTD: default: + goto out; break; } + /* If the sender of this packet is server and we are router we need to + broadcast this packet to other routers in the network. */ + if (broadcast && !server->standalone && server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received New ID packet")); + silc_server_packet_send(server, server->router->connection, + packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + buffer->data, buffer->len, FALSE); + silc_server_backup_send(server, (SilcServerEntry)sock->user_data, + packet->type, packet->flags, + packet->buffer->data, packet->buffer->len, + FALSE, TRUE); + } + out: - silc_notify_payload_free(payload); + silc_id_payload_free(idp); } -/* Received new channel user packet. Information about new users on a - channel are distributed between routers using this packet. The - router receiving this will redistribute it and also sent JOIN notify - to local clients on the same channel. Normal server sends JOIN notify - to its local clients on the channel. */ -void silc_server_new_channel_user(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +/* Processes incoming New ID packet. New ID Payload is used to distribute + information about newly registered clients and servers. */ + +void silc_server_new_id(SilcServer server, SilcSocketConnection sock, + SilcPacketContext *packet) { - unsigned char *tmpid1, *tmpid2; - SilcClientID *client_id = NULL; - SilcChannelID *channel_id = NULL; - unsigned short channel_id_len; - unsigned short client_id_len; - SilcClientEntry client; + silc_server_new_id_real(server, sock, packet, TRUE); +} + +/* 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; + uint16 id_len; + + SILC_LOG_DEBUG(("Processing New ID List")); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + packet->src_id_type != SILC_ID_SERVER) + return; + + /* If the sender of this packet is server and we are router we need to + broadcast this packet to other routers in the network. Broadcast + this list packet instead of multiple New ID packets. */ + if (!server->standalone && server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER && + !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { + SILC_LOG_DEBUG(("Broadcasting received New ID List packet")); + silc_server_packet_send(server, server->router->connection, + packet->type, + packet->flags | SILC_PACKET_FLAG_BROADCAST, + packet->buffer->data, packet->buffer->len, FALSE); + silc_server_backup_send(server, (SilcServerEntry)sock->user_data, + packet->type, packet->flags, + packet->buffer->data, packet->buffer->len, + FALSE, TRUE); + } + + /* 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_real(server, sock, new_id, FALSE); + + 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) +{ + SilcChannelPayload payload; + SilcChannelID *channel_id; + char *channel_name; + uint32 name_len; + unsigned char *id; + uint32 id_len; + uint32 mode; + SilcServerEntry server_entry; SilcChannelEntry channel; - SilcChannelClientEntry chl; - SilcBuffer clidp; - int ret; - SILC_LOG_DEBUG(("Start")); + 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 the channel payload */ + payload = silc_channel_payload_parse(packet->buffer->data, + packet->buffer->len); + if (!payload) + return; + + /* Get the channel ID */ + channel_id = silc_channel_get_id_parse(payload); + if (!channel_id) { + silc_channel_payload_free(payload); + return; + } + + channel_name = silc_channel_get_name(payload, &name_len); + if (name_len > 256) + channel_name[255] = '\0'; + + id = silc_channel_get_id(payload, &id_len); + + server_entry = (SilcServerEntry)sock->user_data; + + if (sock->type == SILC_SOCKET_TYPE_ROUTER) { + /* Add the channel to global list as it is coming from router. It + cannot be our own channel as it is coming from router. */ + + /* 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 (!channel) { + 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, strdup(channel_name), + 0, channel_id, sock->user_data, NULL, NULL, 0); + 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. */ + SilcBuffer chk; + + SILC_LOG_DEBUG(("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 (sock->type == SILC_SOCKET_TYPE_CLIENT || - server->server_type != SILC_ROUTER || - packet->src_id_type != SILC_ID_SERVER) - return; + /* If the channel does not exist, then create it. This creates a new + key to the channel as well that we will send to the server. */ + if (!channel) { + /* The protocol says that the Channel ID's IP address must be based + on the router's IP address. Check whether the ID is based in our + IP and if it is not then create a new ID and enforce the server + to switch the ID. */ + if (server_entry->server_type != SILC_BACKUP_ROUTER && + !SILC_ID_COMPARE(channel_id, server->id, server->id->ip.data_len)) { + SilcChannelID *tmp; + SILC_LOG_DEBUG(("Forcing the server to change Channel ID")); + + if (silc_id_create_channel_id(server, server->id, server->rng, &tmp)) { + silc_server_send_notify_channel_change(server, sock, FALSE, + channel_id, tmp); + silc_free(channel_id); + channel_id = tmp; + } + } - /* Parse payload */ - ret = silc_buffer_unformat(packet->buffer, - SILC_STR_UI16_NSTRING_ALLOC(&tmpid1, - &channel_id_len), - SILC_STR_UI16_NSTRING_ALLOC(&tmpid2, - &client_id_len), - SILC_STR_END); - if (ret == -1) { - if (tmpid1) - silc_free(tmpid1); - if (tmpid2) - silc_free(tmpid2); - return; - } + /* Create the channel with the provided Channel ID */ + channel = silc_server_create_new_channel_with_id(server, NULL, NULL, + channel_name, + channel_id, FALSE); + if (!channel) { + silc_channel_payload_free(payload); + silc_free(channel_id); + return; + } - /* Decode the channel ID */ - channel_id = silc_id_str2id(tmpid1, channel_id_len, SILC_ID_CHANNEL); - if (!channel_id) - goto out; + /* Get the mode and set it to the channel */ + channel->mode = silc_channel_get_mode(payload); - /* Decode the client ID */ - client_id = silc_id_str2id(tmpid2, client_id_len, SILC_ID_CLIENT); - if (!client_id) - goto out; + /* Send the new channel key to the server */ + id = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL); + chk = silc_channel_key_payload_encode(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); - /* Find the channel */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); - if (!channel) { - channel = silc_idlist_find_channel_by_id(server->global_list, - channel_id, NULL); - if (!channel) - goto out; - } + } 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. */ + SilcBuffer users = NULL, users_modes = NULL; - /* If we are router and this packet is not already broadcast packet - we will broadcast it. */ - if (!server->standalone && server->server_type == SILC_ROUTER && - !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received New Channel User packet")); - silc_server_packet_send(server, server->router->connection, packet->type, - packet->flags | SILC_PACKET_FLAG_BROADCAST, - packet->buffer->data, packet->buffer->len, FALSE); - } + if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) { + /* They don't match, send CHANNEL_CHANGE notify to the server to + force the ID change. */ + SILC_LOG_DEBUG(("Forcing the server to change Channel ID")); + silc_server_send_notify_channel_change(server, sock, FALSE, + channel_id, channel->id); + } - /* Get client entry */ - client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL); - if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); - if (!client) - goto out; - } + /* If the mode is different from what we have then enforce the + mode change. */ + mode = silc_channel_get_mode(payload); + if (channel->mode != mode) { + SILC_LOG_DEBUG(("Forcing the server to change channel mode")); + silc_server_send_notify_cmode(server, sock, FALSE, channel, + channel->mode, server->id, + SILC_ID_SERVER, + channel->cipher, channel->hmac_name, + channel->passphrase); + } - /* Join the client to the channel by adding it to channel's user list. - Add also the channel to client entry's channels list for fast cross- - referencing. */ - chl = silc_calloc(1, sizeof(*chl)); - chl->client = client; - chl->channel = channel; - silc_list_add(channel->user_list, chl); - silc_list_add(client->channels, chl); + /* Create new key for the channel and send it to the server and + everybody else possibly on the channel. */ - server->stat.chanclients++; + if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { + if (!silc_server_create_channel_key(server, channel, 0)) + return; + + /* Send to the channel */ + silc_server_send_channel_key(server, sock, channel, FALSE); + id = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL); + + /* Send to the server */ + chk = silc_channel_key_payload_encode(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); + } - /* Send JOIN notify to local clients on the channel. As we are router - it is assured that this is sent only to our local clients and locally - connected servers if needed. */ - clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); - silc_server_send_notify_to_channel(server, sock, channel, FALSE, - SILC_NOTIFY_TYPE_JOIN, - 1, clidp->data, clidp->len); - silc_buffer_free(clidp); + silc_free(channel_id); - client_id = NULL; + /* Since the channel is coming from server and we also know about it + then send the JOIN notify to the server so that it see's our + users on the channel "joining" the channel. */ + silc_server_announce_get_channel_users(server, channel, &users, + &users_modes); + if (users) { + silc_buffer_push(users, users->data - users->head); + silc_server_packet_send(server, sock, + SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, + users->data, users->len, FALSE); + silc_buffer_free(users); + } + if (users_modes) { + silc_buffer_push(users_modes, users_modes->data - users_modes->head); + silc_server_packet_send_dest(server, sock, + SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, + channel->id, SILC_ID_CHANNEL, + users_modes->data, + users_modes->len, FALSE); + silc_buffer_free(users_modes); + } + } + } - out: - if (client_id) - silc_free(client_id); - if (channel_id) - silc_free(channel_id); - silc_free(tmpid1); - silc_free(tmpid2); + silc_channel_payload_free(payload); } -/* Processes incoming REMOVE_ID packet. The packet is used to notify routers - that certain ID should be removed. After that the ID will become invalid. */ +/* 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_remove_id(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_server_new_channel_list(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { - SilcIDList id_list; - SilcIDPayload idp; - SilcIdType id_type; - void *id, *id_entry; + SilcPacketContext *new; + SilcBuffer buffer; + uint16 len1, len2; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Processing New Channel List")); if (sock->type == SILC_SOCKET_TYPE_CLIENT || - server->server_type == SILC_SERVER || - packet->src_id_type != SILC_ID_SERVER) - return; - - idp = silc_id_payload_parse(packet->buffer); - if (!idp) + packet->src_id_type != SILC_ID_SERVER || + server->server_type == SILC_SERVER) return; - id_type = silc_id_payload_get_type(idp); - - id = silc_id_payload_get_id(idp); - if (!id) - goto out; - /* If the sender of this packet is server and we are router we need to - broadcast this packet to other routers in the network. */ + broadcast this packet to other routers in the network. Broadcast + this list packet instead of multiple New Channel packets. */ if (!server->standalone && server->server_type == SILC_ROUTER && sock->type == SILC_SOCKET_TYPE_SERVER && !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received Remove ID packet")); + SILC_LOG_DEBUG(("Broadcasting received New Channel List packet")); silc_server_packet_send(server, server->router->connection, packet->type, packet->flags | SILC_PACKET_FLAG_BROADCAST, packet->buffer->data, packet->buffer->len, FALSE); + silc_server_backup_send(server, (SilcServerEntry)sock->user_data, + packet->type, packet->flags, + packet->buffer->data, packet->buffer->len, + FALSE, TRUE); } - if (sock->type == SILC_SOCKET_TYPE_SERVER) - id_list = server->local_list; - else - id_list = server->global_list; + /* 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; - /* Remove the ID */ - switch (id_type) { - case SILC_ID_CLIENT: - id_entry = silc_idlist_find_client_by_id(id_list, (SilcClientID *)id, - NULL); - if (id_entry) { - /* Remove from channels */ - silc_server_remove_from_channels(server, NULL, id_entry); - - /* Remove the client entry */ - silc_idlist_del_client(id_list, (SilcClientEntry)id_entry); - server->stat.clients--; - if (sock->type == SILC_SOCKET_TYPE_SERVER && - server->server_type == SILC_ROUTER) - server->stat.cell_clients--; - - SILC_LOG_DEBUG(("Removed client id(%s) from [%s] %s", - silc_id_render(id, SILC_ID_CLIENT), - sock->type == SILC_SOCKET_TYPE_SERVER ? - "Server" : "Router", sock->hostname)); - } - break; + buffer = silc_buffer_alloc(512); + new->buffer = buffer; - case SILC_ID_SERVER: - id_entry = silc_idlist_find_server_by_id(id_list, (SilcServerID *)id, - NULL); - if (id_entry) { - silc_idlist_del_server(id_list, (SilcServerEntry)id_entry); - server->stat.servers--; - if (sock->type == SILC_SOCKET_TYPE_SERVER && - server->server_type == SILC_ROUTER) - server->stat.cell_servers--; - - SILC_LOG_DEBUG(("Removed server id(%s) from [%s] %s", - silc_id_render(id, SILC_ID_SERVER), - sock->type == SILC_SOCKET_TYPE_SERVER ? - "Server" : "Router", sock->hostname)); - } - break; + while (packet->buffer->len) { + SILC_GET16_MSB(len1, packet->buffer->data); + if ((len1 > packet->buffer->len) || + (len1 > buffer->truelen)) + break; - case SILC_ID_CHANNEL: - id_entry = silc_idlist_find_channel_by_id(id_list, (SilcChannelID *)id, - NULL); - if (id_entry) { - silc_idlist_del_channel(id_list, (SilcChannelEntry)id_entry); - server->stat.channels--; - if (sock->type == SILC_SOCKET_TYPE_SERVER && - server->server_type == SILC_ROUTER) - server->stat.cell_channels--; - - SILC_LOG_DEBUG(("Removed channel id(%s) from [%s] %s", - silc_id_render(id, SILC_ID_CHANNEL), - sock->type == SILC_SOCKET_TYPE_SERVER ? - "Server" : "Router", sock->hostname)); - } - break; + SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1); + if ((len2 > packet->buffer->len) || + (len2 > buffer->truelen)) + break; - default: - break; + silc_buffer_pull_tail(buffer, 8 + len1 + len2); + silc_buffer_put(buffer, packet->buffer->data, 8 + len1 + len2); + + /* Process the New Channel */ + silc_server_new_channel(server, sock, new); + + silc_buffer_push_tail(buffer, 8 + len1 + len2); + silc_buffer_pull(packet->buffer, 8 + len1 + len2); } - out: - silc_id_payload_free(idp); + silc_buffer_free(buffer); + silc_free(new); } -/* Processes received SET_MODE packet. The packet is used to distribute - the information about changed channel's or client's channel modes. */ +/* Received key agreement packet. This packet is never for us. It is to + the client in the packet's destination ID. Sending of this sort of packet + equals sending private message, ie. it is sent point to point from + one client to another. */ -void silc_server_set_mode(SilcServer server, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_server_key_agreement(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) { - SilcSetModePayload payload = NULL; - SilcArgumentPayload args = NULL; - unsigned short mode_type; - unsigned int mode_mask; - unsigned char *tmp, *tmp2; - unsigned int tmp_len, tmp_len2; - unsigned char mode[4]; - SilcClientID *client_id; - SilcChannelID *channel_id = NULL; - SilcClientEntry client; - SilcChannelEntry channel; - SilcChannelClientEntry chl; - - if (sock->type == SILC_SOCKET_TYPE_CLIENT || - packet->src_id_type == SILC_ID_CLIENT) - return; + SilcSocketConnection dst_sock; + SilcIDListData idata; SILC_LOG_DEBUG(("Start")); - /* If we are router and this packet is not already broadcast packet - we will broadcast it. The sending socket really cannot be router or - the router is buggy. If this packet is coming from router then it must - have the broadcast flag set already and we won't do anything. */ - if (!server->standalone && server->server_type == SILC_ROUTER && - sock->type == SILC_SOCKET_TYPE_SERVER && - !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) { - SILC_LOG_DEBUG(("Broadcasting received Set Mode packet")); - silc_server_packet_send(server, server->router->connection, packet->type, - packet->flags | SILC_PACKET_FLAG_BROADCAST, - packet->buffer->data, packet->buffer->len, FALSE); - } + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT) + return; - /* Parse Set Mode payload */ - payload = silc_set_mode_payload_parse(packet->buffer); - if (!payload) + if (!packet->dst_id) return; - mode_type = silc_set_mode_get_type(payload); - args = silc_set_mode_get_args(payload); - if (!args) - goto out; + /* Get the route to the client */ + dst_sock = silc_server_get_client_route(server, packet->dst_id, + packet->dst_id_len, NULL, &idata); + if (!dst_sock) + return; - mode_mask = silc_set_mode_get_mode(payload); - SILC_PUT32_MSB(mode_mask, mode); + /* Relay the packet */ + silc_server_relay_packet(server, dst_sock, idata->send_key, + idata->hmac_send, idata->psn_send++, + packet, FALSE); +} - switch (mode_type) { - case SILC_MODE_TYPE_CHANNEL: - /* Get Channel 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; +/* Received connection auth request packet that is used during connection + phase to resolve the mandatory authentication method. This packet can + actually be received at anytime but usually it is used only during + the connection authentication phase. Now, protocol says that this packet + can come from client or server, however, we support only this coming + from client and expect that server always knows what authentication + method to use. */ + +void silc_server_connection_auth_request(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcServerConfigClient *client = NULL; + uint16 conn_type; + int ret; + SilcAuthMethod auth_meth = SILC_AUTH_NONE; - /* Get channel entry */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); - if (!channel) { - channel = silc_idlist_find_channel_by_id(server->global_list, - channel_id, NULL); - if (!channel) - goto out; - } + SILC_LOG_DEBUG(("Start")); - /* Get Client ID payload */ - tmp = silc_argument_get_arg_type(args, 2, &tmp_len); - if (!tmp) - goto out; + if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT) + return; - /* Send CMODE_CHANGE notify to local channel */ - silc_server_send_notify_to_channel(server, sock, channel, FALSE, - SILC_NOTIFY_TYPE_CMODE_CHANGE, - 2, tmp, tmp_len, - mode, sizeof(mode)); + /* Parse the payload */ + ret = silc_buffer_unformat(packet->buffer, + SILC_STR_UI_SHORT(&conn_type), + SILC_STR_UI_SHORT(NULL), + SILC_STR_END); + if (ret == -1) + return; - /* Change the mode */ - channel->mode = mode_mask; - break; + if (conn_type != SILC_SOCKET_TYPE_CLIENT) + return; - case SILC_MODE_TYPE_UCHANNEL: - /* Get Channel 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 authentication method for the client */ + auth_meth = SILC_AUTH_NONE; + client = silc_server_config_find_client(server, sock->ip); + if (!client) + client = silc_server_config_find_client(server, sock->hostname); + if (client) { + if (client->passphrase) { + if (client->publickey && !server->config->prefer_passphrase_auth) + auth_meth = SILC_AUTH_PUBLIC_KEY; + else + auth_meth = SILC_AUTH_PASSWORD; + } else if (client->publickey) + auth_meth = SILC_AUTH_PUBLIC_KEY; + } - /* Get channel entry */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); - if (!channel) { - channel = silc_idlist_find_channel_by_id(server->global_list, - channel_id, NULL); - if (!channel) - goto out; - } + /* Send it back to the client */ + silc_server_send_connection_auth_request(server, sock, conn_type, auth_meth); +} - /* Get Client ID payload */ - tmp = silc_argument_get_arg_type(args, 2, &tmp_len); - if (!tmp) - goto out; +/* Received REKEY packet. The sender of the packet wants to regenerate + its session keys. This starts the REKEY protocol. */ - /* Get target Client ID */ - tmp2 = silc_argument_get_arg_type(args, 3, &tmp_len2); - if (!tmp2) - goto out; - client_id = silc_id_payload_parse_id(tmp2, tmp_len2); - if (!client_id) - goto out; +void silc_server_rekey(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcProtocol protocol; + SilcServerRekeyInternalContext *proto_ctx; + SilcIDListData idata = (SilcIDListData)sock->user_data; - /* Get target client entry */ - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); - if (!client) { - client = silc_idlist_find_client_by_id(server->local_list, - client_id, NULL); - if (!client) { - silc_free(client_id); - goto out; - } - } - silc_free(client_id); + SILC_LOG_DEBUG(("Start")); - /* Send CUMODE_CHANGE notify to local channel */ - silc_server_send_notify_to_channel(server, sock, channel, FALSE, - SILC_NOTIFY_TYPE_CUMODE_CHANGE, 2, - tmp, tmp_len, - mode, sizeof(mode), - tmp2, tmp_len2); - - /* Get entry to the channel user list */ - silc_list_start(channel->user_list); - while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) - if (chl->client == client) { - /* Change the mode */ - chl->mode = mode_mask; - break; - } + /* Allocate internal protocol context. This is sent as context + to the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = (void *)server; + proto_ctx->sock = sock; + proto_ctx->responder = TRUE; + proto_ctx->pfs = idata->rekey->pfs; + + /* Perform rekey protocol. Will call the final callback after the + protocol is over. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, + &protocol, proto_ctx, silc_server_rekey_final); + sock->protocol = protocol; + + if (proto_ctx->pfs == FALSE) + /* Run the protocol */ + silc_protocol_execute(protocol, server->schedule, 0, 0); +} - break; +/* Received file transger packet. This packet is never for us. It is to + the client in the packet's destination ID. Sending of this sort of packet + equals sending private message, ie. it is sent point to point from + one client to another. */ - default: - break; - } +void silc_server_ftp(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcSocketConnection dst_sock; + SilcIDListData idata; - out: - if (channel_id) - silc_free(channel_id); - if (args) - silc_argument_payload_free(args); - if (payload) - silc_set_mode_payload_free(payload); + SILC_LOG_DEBUG(("Start")); + + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT) + return; + + if (!packet->dst_id) + return; + + /* Get the route to the client */ + dst_sock = silc_server_get_client_route(server, packet->dst_id, + packet->dst_id_len, NULL, &idata); + if (!dst_sock) + return; + + /* Relay the packet */ + silc_server_relay_packet(server, dst_sock, idata->send_key, + idata->hmac_send, idata->psn_send++, + packet, FALSE); }