X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=apps%2Fsilcd%2Fcommand_reply.c;h=0b1645ec4fba9332a1c8e0dea17eba370916680d;hb=805fddcf6431e784f9f77114782a90c9d12f9cbe;hp=8945bb4191bfba1d34f506380a964354b95e6ffc;hpb=e9b823080bc914df0decfcbcf17865564cf76973;p=silc.git diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 8945bb41..0b1645ec 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -2,15 +2,14 @@ command_reply.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2001 Pekka Riikonen + Copyright (C) 1997 - 2005, 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - + the Free Software Foundation; version 2 of the License. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -23,26 +22,19 @@ #include "server_internal.h" #include "command_reply.h" -/* All functions that call the COMMAND_CHECK_STATUS or the - COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */ - -#define COMMAND_CHECK_STATUS \ -do { \ - SILC_LOG_DEBUG(("Start")); \ - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \ - if (status != SILC_STATUS_OK) \ - goto out; \ -} while(0) - -#define COMMAND_CHECK_STATUS_LIST \ -do { \ - SILC_LOG_DEBUG(("Start")); \ - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \ - if (status != SILC_STATUS_OK && \ - status != SILC_STATUS_LIST_START && \ - status != SILC_STATUS_LIST_ITEM && \ - status != SILC_STATUS_LIST_END) \ - goto out; \ +/* All functions that call the COMMAND_CHECK_STATUS macros must have + out: and err: goto labels. */ + +#define COMMAND_CHECK_STATUS \ +do { \ + SILC_LOG_DEBUG(("Start")); \ + if (!silc_command_get_status(cmd->payload, &status, &error)) { \ + if (SILC_STATUS_IS_ERROR(status)) \ + goto out; \ + if (status == SILC_STATUS_LIST_END) \ + goto out; \ + goto err; \ + } \ } while(0) /* Server command reply list. Not all commands have reply function as @@ -55,8 +47,12 @@ SilcServerCommandReply silc_command_reply_list[] = SILC_SERVER_CMD_REPLY(info, INFO), SILC_SERVER_CMD_REPLY(motd, MOTD), SILC_SERVER_CMD_REPLY(join, JOIN), + SILC_SERVER_CMD_REPLY(stats, STATS), SILC_SERVER_CMD_REPLY(users, USERS), SILC_SERVER_CMD_REPLY(getkey, GETKEY), + SILC_SERVER_CMD_REPLY(list, LIST), + SILC_SERVER_CMD_REPLY(watch, WATCH), + SILC_SERVER_CMD_REPLY(ping, PING), { NULL, 0 }, }; @@ -64,42 +60,49 @@ SilcServerCommandReply silc_command_reply_list[] = /* Process received command reply. */ void silc_server_command_reply_process(SilcServer server, - SilcSocketConnection sock, + SilcPacketStream sock, SilcBuffer buffer) { + SilcIDListData idata = silc_packet_get_context(sock); SilcServerCommandReply *cmd; SilcServerCommandReplyContext ctx; SilcCommandPayload payload; SilcCommand command; - uint16 ident; SILC_LOG_DEBUG(("Start")); /* Get command reply payload from packet */ - payload = silc_command_payload_parse(buffer); + payload = silc_command_payload_parse(buffer->data, silc_buffer_len(buffer)); if (!payload) { /* Silently ignore bad reply packet */ SILC_LOG_DEBUG(("Bad command reply packet")); return; } - + /* Allocate command reply context. This must be free'd by the command reply routine receiving it. */ ctx = silc_calloc(1, sizeof(*ctx)); ctx->server = server; - ctx->sock = silc_socket_dup(sock); + ctx->sock = sock; ctx->payload = payload; ctx->args = silc_command_get_args(ctx->payload); - ident = silc_command_get_ident(ctx->payload); - + ctx->ident = silc_command_get_ident(ctx->payload); + command = silc_command_get(ctx->payload); + silc_packet_stream_ref(sock); + + /* Client is not allowed to send reply to all commands */ + if (idata->conn_type == SILC_CONN_CLIENT && + command != SILC_COMMAND_WHOIS) { + silc_server_command_reply_free(ctx); + return; + } + /* Check for pending commands and mark to be exeucted */ - ctx->callbacks = - silc_server_command_pending_check(server, ctx, - silc_command_get(ctx->payload), - ident, &ctx->callbacks_count); + ctx->callbacks = + silc_server_command_pending_check(server, command, + ctx->ident, &ctx->callbacks_count); /* Execute command reply */ - command = silc_command_get(ctx->payload); for (cmd = silc_command_reply_list; cmd->cb; cmd++) if (cmd->cmd == command) break; @@ -119,52 +122,86 @@ void silc_server_command_reply_free(SilcServerCommandReplyContext cmd) if (cmd) { silc_command_payload_free(cmd->payload); if (cmd->sock) - silc_socket_free(cmd->sock); /* Decrease the reference counter */ + silc_packet_stream_unref(cmd->sock); silc_free(cmd->callbacks); silc_free(cmd); } } +static void +silc_server_command_process_error(SilcServerCommandReplyContext cmd, + SilcStatus error) +{ + SilcServer server = cmd->server; + SilcIDListData idata = silc_packet_get_context(cmd->sock); + + /* If we received notify for invalid ID we'll remove the ID if we + have it cached. */ + if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID && + idata->conn_type == SILC_CONN_ROUTER) { + SilcClientEntry client; + SilcID id; + if (silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, + NULL)) { + SILC_LOG_DEBUG(("Received invalid client ID notification, deleting " + "the entry from cache")); + client = silc_idlist_find_client_by_id(server->global_list, + SILC_ID_GET_ID(id), FALSE, NULL); + if (!client) + return; + + silc_server_remove_from_channels(server, NULL, client, TRUE, + NULL, TRUE, FALSE); + silc_dlist_del(server->expired_clients, client); + silc_idlist_del_data(client); + silc_idlist_del_client(server->global_list, client); + } + } +} + /* Caches the received WHOIS information. */ static char silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - unsigned char *tmp, *id_data; - char *nickname, *username, *realname, *servername = NULL; - SilcClientID *client_id; + unsigned char *id_data, *umodes; + char *nickname, *username, *realname, *tmp; + unsigned char *fingerprint; + SilcID id; SilcClientEntry client; char global = FALSE; - char *nick; - uint32 mode = 0, len, id_len; + char nick[128 + 1], servername[256 + 1], uname[128 + 1]; + SilcUInt32 mode = 0, len, len2, id_len, flen; + const char *hostname, *ip; + + silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock), + NULL, &hostname, &ip, NULL); id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); nickname = silc_argument_get_arg_type(cmd->args, 3, &len); username = silc_argument_get_arg_type(cmd->args, 4, &len); realname = silc_argument_get_arg_type(cmd->args, 5, &len); - if (!id_data || !nickname || !username || !realname) { - SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s", - nickname ? nickname : "", - username ? username : "", - realname ? realname : "")); + if (!id_data || !nickname || !username || !realname) return FALSE; - } tmp = silc_argument_get_arg_type(cmd->args, 7, &len); if (tmp) SILC_GET32_MSB(mode, tmp); - client_id = silc_id_payload_parse_id(id_data, id_len); - if (!client_id) + if (!silc_id_payload_parse_id(id_data, id_len, &id)) return FALSE; + fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen); + /* Check if we have this client cached already. */ - client = silc_idlist_find_client_by_id(server->local_list, client_id, + client = silc_idlist_find_client_by_id(server->local_list, + SILC_ID_GET_ID(id), FALSE, NULL); if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, client_id, + client = silc_idlist_find_client_by_id(server->global_list, + SILC_ID_GET_ID(id), FALSE, NULL); global = TRUE; } @@ -172,61 +209,189 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + if (server->server_type != SILC_SERVER) return FALSE; /* Take hostname out of nick string if it includes it. */ - silc_parse_userfqdn(nickname, &nick, &servername); + silc_parse_userfqdn(nickname, nick, sizeof(nick), servername, + sizeof(servername)); /* We don't have that client anywhere, add it. The client is added - to global list since server didn't have it in the lists so it must be - global. */ - client = silc_idlist_add_client(server->global_list, nick, - strdup(username), - strdup(realname), client_id, - cmd->sock->user_data, NULL); + to global list since server didn't have it in the lists so it must be + global. This will check for valid nickname and username strings. */ + client = silc_idlist_add_client(server->global_list, + strdup(nick), username, + strdup(realname), + silc_id_dup(SILC_ID_GET_ID(id), + SILC_ID_CLIENT), + silc_packet_get_context(cmd->sock), + NULL); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); return FALSE; } - client->data.status |= + client->data.status |= (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED); client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; client->mode = mode; - client->servername = servername; + client->servername = servername[0] ? strdup(servername) : NULL; } else { /* We have the client already, update the data */ SILC_LOG_DEBUG(("Updating client data")); - /* Take hostname out of nick string if it includes it. */ - silc_parse_userfqdn(nickname, &nick, &servername); + /* Check nickname */ + silc_parse_userfqdn(nickname, nick, sizeof(nick), servername, + sizeof(servername)); + nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!nickname) { + SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOIS reply " + "from %s", + hostname ? hostname : "", nick)); + return FALSE; + } - /* Remove the old cache entry */ - silc_idcache_del_by_context(global ? server->global_list->clients : - server->local_list->clients, client); + /* Check username */ + silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0); + if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128)) { + SILC_LOG_ERROR(("Malformed username '%s' received in WHOIS reply " + "from %s", + hostname ? hostname : "", tmp)); + return FALSE; + } + + /* Update entry */ + silc_idcache_update_by_context(global ? server->global_list->clients : + server->local_list->clients, client, NULL, + nickname, TRUE); silc_free(client->nickname); silc_free(client->username); silc_free(client->userinfo); silc_free(client->servername); - - client->nickname = nick; + + client->nickname = strdup(nick); client->username = strdup(username); client->userinfo = strdup(realname); - client->servername = servername; + client->servername = servername[0] ? strdup(servername) : NULL; client->mode = mode; client->data.status |= SILC_IDLIST_STATUS_RESOLVED; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + } + + /* Save channel list if it was sent to us */ + if (server->server_type == SILC_SERVER) { + tmp = silc_argument_get_arg_type(cmd->args, 6, &len); + umodes = silc_argument_get_arg_type(cmd->args, 10, &len2); + if (tmp && umodes) { + SilcBufferStruct channels_buf, umodes_buf; + silc_buffer_set(&channels_buf, tmp, len); + silc_buffer_set(&umodes_buf, umodes, len2); + silc_server_save_user_channels(server, cmd->sock, client, &channels_buf, + &umodes_buf); + } else { + silc_server_save_user_channels(server, cmd->sock, client, NULL, NULL); + } + } + + if (fingerprint && flen == sizeof(client->data.fingerprint)) + memcpy(client->data.fingerprint, fingerprint, flen); + + /* Take Requested Attributes if set. */ + tmp = silc_argument_get_arg_type(cmd->args, 11, &len); + if (tmp) { + silc_free(client->attrs); + client->attrs = silc_memdup(tmp, len); + client->attrs_len = len; + + /* Try to take public key from attributes if present and we don't have + the key already. Do this only on normal server. Routers do GETKEY + for all clients anyway. */ + if (server->server_type != SILC_ROUTER && !client->data.public_key) { + SilcAttributePayload attr; + SilcAttributeObjPk pk; + unsigned char f[SILC_HASH_MAXLEN]; + SilcDList attrs = silc_attribute_payload_parse(tmp, len); + + SILC_LOG_DEBUG(("Take client public key from attributes")); + + if (attrs) { + silc_dlist_start(attrs); + while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) { + if (silc_attribute_get_attribute(attr) == + SILC_ATTRIBUTE_USER_PUBLIC_KEY) { + + if (!silc_attribute_get_object(attr, &pk, sizeof(pk))) + continue; + + /* Take only SILC public keys */ + if (strcmp(pk.type, "silc-rsa")) { + silc_free(pk.type); + silc_free(pk.data); + continue; + } + + /* Verify that the server provided fingerprint matches the key */ + silc_hash_make(server->sha1hash, pk.data, pk.data_len, f); + if (memcmp(f, client->data.fingerprint, sizeof(f))) { + silc_free(pk.type); + silc_free(pk.data); + continue; + } + + /* Save the public key. */ + if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC, + pk.data, pk.data_len, + &client->data.public_key)) { + silc_free(pk.type); + silc_free(pk.data); + continue; + } + + SILC_LOG_DEBUG(("Saved client public key from attributes")); + + /* Add client's public key to repository */ + if (!silc_server_get_public_key_by_client(server, client, NULL)) + silc_skr_add_public_key_simple(server->repository, + client->data.public_key, + SILC_SKR_USAGE_IDENTIFICATION, + client, NULL); + + silc_free(pk.type); + silc_free(pk.data); + break; + } + } + + silc_attribute_payload_list_free(attrs); + } + } + } + + return TRUE; +} - /* Create new cache entry */ - silc_idcache_add(global ? server->global_list->clients : - server->local_list->clients, nick, client->id, - client, FALSE); - silc_free(client_id); +/* Handle requested attributes reply in WHOIS from client */ + +static char +silc_server_command_reply_whois_save_client(SilcServerCommandReplyContext cmd) +{ + unsigned char *tmp; + SilcUInt32 len; + SilcClientEntry client = silc_packet_get_context(cmd->sock); + + /* Take Requested Attributes if set. */ + tmp = silc_argument_get_arg_type(cmd->args, 11, &len); + if (tmp && client) { + silc_free(client->attrs); + client->attrs = silc_memdup(tmp, len); + client->attrs_len = len; } + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + return TRUE; } @@ -239,12 +404,18 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) SILC_SERVER_CMD_REPLY_FUNC(whois) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; - SilcCommandStatus status; + SilcIDListData idata = silc_packet_get_context(cmd->sock); + SilcStatus status, error; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; - if (!silc_server_command_reply_whois_save(cmd)) - goto out; + if (idata->conn_type != SILC_CONN_CLIENT) { + if (!silc_server_command_reply_whois_save(cmd)) + goto out; + } else { + if (!silc_server_command_reply_whois_save_client(cmd)) + goto out; + } /* Pending callbacks are not executed if this was an list entry */ if (status != SILC_STATUS_OK && @@ -254,8 +425,13 @@ SILC_SERVER_CMD_REPLY_FUNC(whois) } out: + silc_server_command_process_error(cmd, error); SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS); + silc_server_command_reply_free(cmd); + return; + + err: + silc_server_command_process_error(cmd, error); silc_server_command_reply_free(cmd); } @@ -265,14 +441,18 @@ static char silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - uint32 len, id_len; + SilcUInt32 len, id_len; unsigned char *id_data; - char *nickname, *username, *realname, *servername = NULL; - SilcClientID *client_id; + char *nickname, *username, *realname; + SilcID id; SilcClientEntry client; SilcIDCacheEntry cache = NULL; - char *nick; + char nick[128 + 1], servername[256 + 1], uname[128 + 1]; int global = FALSE; + const char *hostname, *ip; + + silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock), + NULL, &hostname, &ip, NULL); id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); nickname = silc_argument_get_arg_type(cmd->args, 3, &len); @@ -282,68 +462,97 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) realname = silc_argument_get_arg_type(cmd->args, 5, &len); - client_id = silc_id_payload_parse_id(id_data, id_len); - if (!client_id) + if (!silc_id_payload_parse_id(id_data, id_len, &id)) return FALSE; /* Check if we have this client cached already. */ - client = silc_idlist_find_client_by_id(server->local_list, client_id, + client = silc_idlist_find_client_by_id(server->local_list, + SILC_ID_GET_ID(id), FALSE, &cache); if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, - client_id, FALSE, &cache); + client = silc_idlist_find_client_by_id(server->global_list, + SILC_ID_GET_ID(id), + FALSE, &cache); global = TRUE; } if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + if (server->server_type != SILC_SERVER) return FALSE; /* Take hostname out of nick string if it includes it. */ - silc_parse_userfqdn(nickname, &nick, &servername); + silc_parse_userfqdn(nickname, nick, sizeof(nick), servername, + sizeof(servername)); /* We don't have that client anywhere, add it. The client is added - to global list since server didn't have it in the lists so it must be + to global list since server didn't have it in the lists so it must be global. */ - client = silc_idlist_add_client(server->global_list, nick, - strdup(username), strdup(realname), - silc_id_dup(client_id, SILC_ID_CLIENT), - cmd->sock->user_data, NULL); + client = silc_idlist_add_client(server->global_list, + strdup(nick), username, + strdup(realname), + silc_id_dup(SILC_ID_GET_ID(id), + SILC_ID_CLIENT), + silc_packet_get_context(cmd->sock), NULL); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); return FALSE; } - client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; - client = silc_idlist_find_client_by_id(server->global_list, - client_id, TRUE, &cache); - cache->expire = SILC_ID_CACHE_EXPIRE_DEF; - client->servername = servername; + client->data.status |= SILC_IDLIST_STATUS_RESOLVED; + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; + client->servername = servername[0] ? strdup(servername) : NULL; } else { /* We have the client already, update the data */ - /* Take hostname out of nick string if it includes it. */ - silc_parse_userfqdn(nickname, &nick, &servername); + /* Check nickname */ + silc_parse_userfqdn(nickname, nick, sizeof(nick), servername, + sizeof(servername)); + nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!nickname) { + SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOWAS reply " + "from %s", + nick, hostname ? hostname : "")); + return FALSE; + } + + /* Check username */ + silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0); + if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128)) + return FALSE; silc_free(client->nickname); silc_free(client->username); - - client->nickname = nick; + silc_free(client->servername); + + client->nickname = strdup(nick); client->username = strdup(username); - client->servername = servername; - - /* Remove the old cache entry and create a new one */ - silc_idcache_del_by_context(global ? server->global_list->clients : - server->local_list->clients, client); - silc_idcache_add(global ? server->global_list->clients : - server->local_list->clients, nick, client->id, - client, FALSE); + client->servername = servername[0] ? strdup(servername) : NULL; + client->data.status |= SILC_IDLIST_STATUS_RESOLVED; + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; + + /* Update cache entry */ + silc_idcache_update_by_context(global ? server->global_list->clients : + server->local_list->clients, client, NULL, + nickname, TRUE); } - silc_free(client_id); + /* If client is global and is not on any channel then add that we'll + expire the entry after a while. */ + if (global) { + client = silc_idlist_find_client_by_id(server->global_list, client->id, + FALSE, &cache); + if (client && !silc_hash_table_count(client->channels)) { + client->data.created = silc_time(); + silc_dlist_del(server->expired_clients, client); + silc_dlist_add(server->expired_clients, client); + } + } return TRUE; } @@ -354,9 +563,9 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) SILC_SERVER_CMD_REPLY_FUNC(whowas) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; - SilcCommandStatus status; + SilcStatus status, error; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; if (!silc_server_command_reply_whowas_save(cmd)) goto out; @@ -369,8 +578,13 @@ SILC_SERVER_CMD_REPLY_FUNC(whowas) } out: + silc_server_command_process_error(cmd, error); SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS); + silc_server_command_reply_free(cmd); + return; + + err: + silc_server_command_process_error(cmd, error); silc_server_command_reply_free(cmd); } @@ -380,24 +594,24 @@ static char silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - uint32 len, id_len; + SilcUInt32 len, id_len; unsigned char *id_data; char *name, *info; - SilcClientID *client_id = NULL; - SilcServerID *server_id = NULL; - SilcChannelID *channel_id = NULL; + SilcClientID client_id; + SilcServerID server_id; + SilcChannelID*channel_id; SilcClientEntry client; SilcServerEntry server_entry; SilcChannelEntry channel; char global = FALSE; - char *nick = NULL; + char nick[128 + 1]; SilcIDPayload idp = NULL; SilcIdType id_type; id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); if (!id_data) return FALSE; - idp = silc_id_payload_parse_data(id_data, id_len); + idp = silc_id_payload_parse(id_data, id_len); if (!idp) return FALSE; @@ -408,59 +622,71 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) switch (id_type) { case SILC_ID_CLIENT: - client_id = silc_id_payload_get_id(idp); - if (!client_id) + if (!silc_id_payload_get_id(idp, &client_id, sizeof(client_id))) goto error; SILC_LOG_DEBUG(("Received client information")); - client = silc_idlist_find_client_by_id(server->local_list, - client_id, FALSE, NULL); + client = silc_idlist_find_client_by_id(server->local_list, + &client_id, FALSE, NULL); if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, client_id, + client = silc_idlist_find_client_by_id(server->global_list, &client_id, FALSE, NULL); global = TRUE; } if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + if (server->server_type != SILC_SERVER) goto error; /* Take nickname */ if (name) - silc_parse_userfqdn(name, &nick, NULL); + silc_parse_userfqdn(name, nick, sizeof(nick), NULL, 0); /* We don't have that client anywhere, add it. The client is added - to global list since server didn't have it in the lists so it must be + to global list since server didn't have it in the lists so it must be global. */ - client = silc_idlist_add_client(server->global_list, nick, - info ? strdup(info) : NULL, NULL, - client_id, cmd->sock->user_data, NULL); + client = silc_idlist_add_client(server->global_list, + nick[0] ? nick : NULL, info, NULL, + silc_id_dup(&client_id, SILC_ID_CLIENT), + silc_packet_get_context(cmd->sock), + NULL); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); goto error; } + client->data.status |= SILC_IDLIST_STATUS_REGISTERED; client->data.status |= SILC_IDLIST_STATUS_RESOLVED; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; } else { /* We have the client already, update the data */ - + SILC_LOG_DEBUG(("Updating client data")); - + /* Take nickname */ if (name) { - silc_parse_userfqdn(name, &nick, NULL); + silc_parse_userfqdn(name, nick, sizeof(nick), NULL, 0); - /* Remove the old cache entry */ - silc_idcache_del_by_context(global ? server->global_list->clients : - server->local_list->clients, client); + /* Check nickname */ + name = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, + 128, NULL); + if (!name) { + SILC_LOG_ERROR(("Malformed nickname '%s' received in IDENTIFY " + "reply ", nick)); + return FALSE; + } silc_free(client->nickname); - client->nickname = nick; + client->nickname = strdup(nick); + + /* Update the context */ + silc_idcache_update_by_context(global ? server->global_list->clients : + server->local_list->clients, client, + NULL, name, TRUE); } - + if (info) { silc_free(client->username); client->username = strdup(info); @@ -468,85 +694,88 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) client->data.status |= SILC_IDLIST_STATUS_RESOLVED; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; - - if (name) { - /* Add new cache entry */ - silc_idcache_add(global ? server->global_list->clients : - server->local_list->clients, nick, client->id, - client, FALSE); - } - - silc_free(client_id); } break; case SILC_ID_SERVER: - server_id = silc_id_payload_get_id(idp); - if (!server_id) + if (!name) + goto error; + + if (!silc_id_payload_get_id(idp, &server_id, sizeof(server_id))) goto error; SILC_LOG_DEBUG(("Received server information")); - server_entry = silc_idlist_find_server_by_id(server->local_list, - server_id, FALSE, NULL); + server_entry = silc_idlist_find_server_by_id(server->local_list, + &server_id, FALSE, NULL); if (!server_entry) - server_entry = silc_idlist_find_server_by_id(server->global_list, - server_id, FALSE, NULL); + server_entry = silc_idlist_find_server_by_id(server->global_list, + &server_id, FALSE, NULL); if (!server_entry) { /* If router did not find such Server ID in its lists then this must - be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + be bogus server or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) goto error; - + /* We don't have that server anywhere, add it. */ - server_entry = silc_idlist_add_server(server->global_list, + server_entry = silc_idlist_add_server(server->global_list, strdup(name), 0, - server_id, NULL, NULL); - if (!server_entry) { - silc_free(server_id); + silc_id_dup(&server_id, + SILC_ID_SERVER), + server->router, + SILC_PRIMARY_ROUTE(server)); + if (!server_entry) goto error; - } + server_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED; server_entry->data.status |= SILC_IDLIST_STATUS_RESOLVED; server_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; - server_id = NULL; } - silc_free(server_id); break; case SILC_ID_CHANNEL: - channel_id = silc_id_payload_get_id(idp); - if (!channel_id) + if (!name) + goto error; + + if (!silc_id_payload_get_id(idp, &channel_id, sizeof(channel_id))) goto error; SILC_LOG_DEBUG(("Received channel information")); - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); + /* Check channel name */ + info = silc_channel_name_check(name, strlen(name), SILC_STRING_UTF8, + 256, NULL); + if (!info) + goto error; + + channel = silc_idlist_find_channel_by_name(server->local_list, + info, NULL); if (!channel) - channel = silc_idlist_find_channel_by_id(server->global_list, channel_id, - NULL); + channel = silc_idlist_find_channel_by_name(server->global_list, + info, NULL); if (!channel) { - /* If router did not find such Server ID in its lists then this must - be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + /* If router did not find such Channel ID in its lists then this must + be bogus channel or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) { + silc_free(info); goto error; - - /* We don't have that server anywhere, add it. */ + } + + /* We don't have that channel anywhere, add it. */ channel = silc_idlist_add_channel(server->global_list, strdup(name), - SILC_CHANNEL_MODE_NONE, channel_id, - server->router->connection, - NULL, NULL); + SILC_CHANNEL_MODE_NONE, + silc_id_dup(&channel_id, + SILC_ID_CHANNEL), + server->router, NULL, NULL, 0); if (!channel) { - silc_free(channel_id); + silc_free(info); goto error; } - channel_id = NULL; + silc_free(info); } - silc_free(channel_id); break; } @@ -566,9 +795,9 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) SILC_SERVER_CMD_REPLY_FUNC(identify) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; - SilcCommandStatus status; + SilcStatus status, error; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; if (!silc_server_command_reply_identify_save(cmd)) goto out; @@ -581,8 +810,13 @@ SILC_SERVER_CMD_REPLY_FUNC(identify) } out: + silc_server_command_process_error(cmd, error); SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY); + silc_server_command_reply_free(cmd); + return; + + err: + silc_server_command_process_error(cmd, error); silc_server_command_reply_free(cmd); } @@ -592,41 +826,40 @@ SILC_SERVER_CMD_REPLY_FUNC(info) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcServerEntry entry; - SilcServerID *server_id; - uint32 tmp_len; + SilcID id; + SilcUInt32 tmp_len; unsigned char *tmp, *name; COMMAND_CHECK_STATUS; /* Get Server ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); - if (!tmp) - goto out; - server_id = silc_id_payload_parse_id(tmp, tmp_len); - if (!server_id) + if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) goto out; /* Get the name */ name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); - if (tmp_len > 256) + if (!name) goto out; - entry = silc_idlist_find_server_by_id(server->local_list, server_id, + entry = silc_idlist_find_server_by_id(server->local_list, + SILC_ID_GET_ID(id), FALSE, NULL); if (!entry) { - entry = silc_idlist_find_server_by_id(server->global_list, server_id, + entry = silc_idlist_find_server_by_id(server->global_list, + SILC_ID_GET_ID(id), FALSE, NULL); if (!entry) { /* Add the server to global list */ - server_id = silc_id_dup(server_id, SILC_ID_SERVER); - entry = silc_idlist_add_server(server->global_list, name, 0, - server_id, NULL, NULL); - if (!entry) { - silc_free(server_id); + entry = silc_idlist_add_server(server->global_list, strdup(name), 0, + silc_id_dup(SILC_ID_GET_ID(id), + SILC_ID_SERVER), + silc_packet_get_context(cmd->sock), + cmd->sock); + if (!entry) goto out; - } + entry->data.status |= SILC_IDLIST_STATUS_REGISTERED; } } @@ -640,7 +873,7 @@ SILC_SERVER_CMD_REPLY_FUNC(info) out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO); + err: silc_server_command_reply_free(cmd); } @@ -650,29 +883,55 @@ SILC_SERVER_CMD_REPLY_FUNC(motd) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcServerEntry entry = NULL; - SilcServerID *server_id; - uint32 tmp_len; + SilcID id; + SilcUInt32 tmp_len; unsigned char *tmp; COMMAND_CHECK_STATUS; /* Get Server ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); - if (!tmp) - goto out; - server_id = silc_id_payload_parse_id(tmp, tmp_len); - if (!server_id) + if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) goto out; - entry = silc_idlist_find_server_by_id(server->local_list, server_id, + entry = silc_idlist_find_server_by_id(server->local_list, + SILC_ID_GET_ID(id), TRUE, NULL); if (!entry) { - entry = silc_idlist_find_server_by_id(server->global_list, server_id, + entry = silc_idlist_find_server_by_id(server->global_list, + SILC_ID_GET_ID(id), TRUE, NULL); - if (!entry) - goto out; + if (!entry) { + SilcBuffer buffer; + + /* If router did not find such Server ID in its lists then this must + be bogus client or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) + goto out; + + /* Statistics */ + cmd->server->stat.commands_sent++; + + /* entry isn't known so we IDENTIFY it. otherwise the + silc_server_command_motd won't know about it and tell + the client that there is no such server */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, + ++server->cmd_ident, 5, + 1, NULL, 0, 2, NULL, 0, + 3, NULL, 0, 4, NULL, 0, + 5, tmp, tmp_len); + silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server), + SILC_PACKET_COMMAND, 0, buffer->data, + silc_buffer_len(buffer)); + silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, + server->cmd_ident, + silc_server_command_reply_motd, + cmd); + silc_buffer_free(buffer); + return; + } } /* Get the motd */ @@ -684,7 +943,7 @@ SILC_SERVER_CMD_REPLY_FUNC(motd) out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD); + err: silc_server_command_reply_free(cmd); if (entry) @@ -700,16 +959,15 @@ SILC_SERVER_CMD_REPLY_FUNC(join) SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; SilcIDCacheEntry cache = NULL; - SilcCommandStatus status; - SilcChannelID *id; - SilcClientID *client_id = NULL; + SilcStatus status, error; + SilcID id, id2; SilcChannelEntry entry; SilcHmac hmac = NULL; - uint32 id_len, len, list_count; - unsigned char *id_string; - char *channel_name, *tmp; - uint32 mode, created; + SilcUInt32 len, list_count; + char *channel_name, *channel_namec = NULL, *tmp; + SilcUInt32 mode, created; SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL; + SilcPublicKey founder_key = NULL; COMMAND_CHECK_STATUS; @@ -719,16 +977,11 @@ SILC_SERVER_CMD_REPLY_FUNC(join) goto out; /* Get channel ID */ - id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len); - if (!id_string) + if (!silc_argument_get_decoded(cmd->args, 3, SILC_ARGUMENT_ID, &id, NULL)) goto out; /* Get client ID */ - tmp = silc_argument_get_arg_type(cmd->args, 4, &len); - if (!tmp) - goto out; - client_id = silc_id_payload_parse_id(tmp, len); - if (!client_id) + if (!silc_argument_get_decoded(cmd->args, 4, SILC_ARGUMENT_ID, &id2, NULL)) goto out; /* Get mode mask */ @@ -749,14 +1002,10 @@ SILC_SERVER_CMD_REPLY_FUNC(join) tmp = silc_argument_get_arg_type(cmd->args, 7, &len); if (tmp) { keyp = silc_buffer_alloc(len); - silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp)); + silc_buffer_pull_tail(keyp, silc_buffer_truelen(keyp)); silc_buffer_put(keyp, tmp, len); } - id = silc_id_payload_parse_id(id_string, id_len); - if (!id) - goto out; - /* Get hmac */ tmp = silc_argument_get_arg_type(cmd->args, 11, NULL); if (tmp) { @@ -788,157 +1037,260 @@ SILC_SERVER_CMD_REPLY_FUNC(join) silc_buffer_pull_tail(client_mode_list, len); silc_buffer_put(client_mode_list, tmp, len); + /* Get founder key */ + tmp = silc_argument_get_arg_type(cmd->args, 15, &len); + if (tmp) + silc_public_key_payload_decode(tmp, len, &founder_key); + /* See whether we already have the channel. */ - entry = silc_idlist_find_channel_by_name(server->local_list, - channel_name, &cache); + channel_namec = silc_channel_name_check(channel_name, strlen(channel_name), + SILC_STRING_UTF8, 256, NULL); + if (!channel_namec) + goto out; + entry = silc_idlist_find_channel_by_name(server->local_list, + channel_namec, &cache); if (!entry) { /* Add new channel */ - SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", + SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", (created == 0 ? "existing" : "created"), channel_name, - silc_id_render(id, SILC_ID_CHANNEL))); + silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL))); + + /* If the channel is found from global list we must move it to the + local list. */ + entry = silc_idlist_find_channel_by_name(server->global_list, + channel_namec, &cache); + if (entry) + silc_idlist_del_channel(server->global_list, entry); /* Add the channel to our local list. */ - entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), - SILC_CHANNEL_MODE_NONE, id, - server->router, NULL, hmac); - if (!entry) { - silc_free(id); + entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), + SILC_CHANNEL_MODE_NONE, + silc_id_dup(SILC_ID_GET_ID(id), + SILC_ID_CHANNEL), + server->router, NULL, NULL, hmac); + if (!entry) goto out; - } + + hmac = NULL; + server->stat.my_channels++; + server->stat.channels++; } else { /* The entry exists. */ - if (cache->id) - silc_free(cache->id); - entry->id = id; - cache->id = entry->id; + + /* If ID has changed, then update it to the cache too. */ + if (!SILC_ID_CHANNEL_COMPARE(entry->id, SILC_ID_GET_ID(id))) + silc_idlist_replace_channel_id(server->local_list, entry->id, + SILC_ID_GET_ID(id)); + + entry->disabled = FALSE; /* Remove the founder auth data if the mode is not set but we have them in the entry */ if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) { silc_pkcs_public_key_free(entry->founder_key); - if (entry->founder_passwd) { - silc_free(entry->founder_passwd); - entry->founder_passwd = NULL; - } + entry->founder_key = NULL; } } - if (entry->hmac_name && hmac) { + if (founder_key) { + if (entry->founder_key) + silc_pkcs_public_key_free(entry->founder_key); + entry->founder_key = founder_key; + founder_key = NULL; + } + + if (entry->hmac_name && (hmac || (!hmac && entry->hmac))) { silc_free(entry->hmac_name); - entry->hmac_name = strdup(hmac->hmac->name); + entry->hmac_name = strdup(silc_hmac_get_name(hmac ? hmac : entry->hmac)); } /* Get the ban list */ tmp = silc_argument_get_arg_type(cmd->args, 8, &len); - if (tmp) { - if (entry->ban_list) - silc_free(entry->ban_list); - entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list)); - memcpy(entry->ban_list, tmp, len); + if (tmp && len > 2) { + SilcArgumentPayload iargs; + SilcUInt16 iargc; + SILC_GET16_MSB(iargc, tmp); + iargs = silc_argument_payload_parse(tmp + 2, len - 2, iargc); + if (iargs) { + /* Delete old ban list */ + if (entry->ban_list) + silc_hash_table_free(entry->ban_list); + entry->ban_list = + silc_hash_table_alloc(0, silc_hash_ptr, + NULL, NULL, NULL, + silc_server_inviteban_destruct, entry, TRUE); + + /* Add new ban list */ + silc_server_inviteban_process(server, entry->ban_list, 0, iargs); + silc_argument_payload_free(iargs); + } } /* Get the invite list */ tmp = silc_argument_get_arg_type(cmd->args, 9, &len); - if (tmp) { - if (entry->invite_list) - silc_free(entry->invite_list); - entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list)); - memcpy(entry->invite_list, tmp, len); + if (tmp && len > 2) { + SilcArgumentPayload iargs; + SilcUInt16 iargc; + SILC_GET16_MSB(iargc, tmp); + iargs = silc_argument_payload_parse(tmp + 2, len - 2, iargc); + if (iargs) { + /* Delete old invite list */ + if (entry->invite_list) + silc_hash_table_free(entry->invite_list); + entry->invite_list = + silc_hash_table_alloc(0, silc_hash_ptr, + NULL, NULL, NULL, + silc_server_inviteban_destruct, entry, TRUE); + + /* Add new invite list */ + silc_server_inviteban_process(server, entry->invite_list, 0, iargs); + silc_argument_payload_free(iargs); + } } /* Get the topic */ tmp = silc_argument_get_arg_type(cmd->args, 10, &len); if (tmp) { - if (entry->topic) - silc_free(entry->topic); + silc_free(entry->topic); entry->topic = strdup(tmp); } - /* If channel was not created we know there is global users on the + /* Get channel public key list */ + tmp = silc_argument_get_arg_type(cmd->args, 16, &len); + if (tmp && server->server_type != SILC_ROUTER) + silc_server_set_channel_pk_list(server, NULL, entry, tmp, len); + + /* The the user limit */ + tmp = silc_argument_get_arg_type(cmd->args, 17, &len); + if (tmp && len == 4) + SILC_GET32_MSB(entry->user_limit, tmp); + + /* If channel was not created we know there is global users on the channel. */ entry->global_users = (created == 0 ? TRUE : FALSE); /* If channel was just created the mask must be zero */ - if (!entry->global_users && mode) { - SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for " - "new channel, forcing it to zero", cmd->sock->hostname)); + if (!entry->global_users && mode) mode = 0; - } /* Save channel mode */ entry->mode = mode; /* Save channel key */ - if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) - silc_server_save_channel_key(server, keyp, entry); - if (keyp) + if (keyp) { + if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) + silc_server_save_channel_key(server, keyp, entry); silc_buffer_free(keyp); + } /* Save the users to the channel */ - silc_server_save_users_on_channel(server, cmd->sock, entry, - client_id, client_id_list, + silc_server_save_users_on_channel(server, cmd->sock, entry, + SILC_ID_GET_ID(id2), client_id_list, client_mode_list, list_count); + entry->users_resolved = TRUE; out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN); - silc_free(client_id); + err: + silc_free(channel_namec); + if (hmac) + silc_hmac_free(hmac); silc_server_command_reply_free(cmd); + if (founder_key) + silc_pkcs_public_key_free(founder_key); if (client_id_list) silc_buffer_free(client_id_list); if (client_mode_list) silc_buffer_free(client_mode_list); } +/* Received reply to STATS command. */ + +SILC_SERVER_CMD_REPLY_FUNC(stats) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcStatus status, error; + unsigned char *tmp; + SilcUInt32 tmp_len; + SilcBufferStruct buf; + + COMMAND_CHECK_STATUS; + + /* Get statistics structure */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); + if (server->server_type != SILC_ROUTER && tmp) { + silc_buffer_set(&buf, tmp, tmp_len); + silc_buffer_unformat(&buf, + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(&server->stat.cell_clients), + SILC_STR_UI_INT(&server->stat.cell_channels), + SILC_STR_UI_INT(&server->stat.cell_servers), + SILC_STR_UI_INT(&server->stat.clients), + SILC_STR_UI_INT(&server->stat.channels), + SILC_STR_UI_INT(&server->stat.servers), + SILC_STR_UI_INT(&server->stat.routers), + SILC_STR_UI_INT(&server->stat.server_ops), + SILC_STR_UI_INT(&server->stat.router_ops), + SILC_STR_END); + } + + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_STATS); + err: + silc_server_command_reply_free(cmd); +} + SILC_SERVER_CMD_REPLY_FUNC(users) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcChannelEntry channel; - SilcChannelID *channel_id = NULL; + SilcID id; SilcBuffer client_id_list; SilcBuffer client_mode_list; unsigned char *tmp; - uint32 tmp_len; - uint32 list_count; + SilcUInt32 tmp_len; + SilcUInt32 list_count; COMMAND_CHECK_STATUS; /* Get channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); - if (!tmp) - goto out; - channel_id = silc_id_payload_parse_id(tmp, tmp_len); - if (!channel_id) + if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) goto out; /* Get channel entry */ - channel = silc_idlist_find_channel_by_id(server->local_list, - channel_id, NULL); + channel = silc_idlist_find_channel_by_id(server->local_list, + SILC_ID_GET_ID(id), NULL); if (!channel) { - channel = silc_idlist_find_channel_by_id(server->global_list, - channel_id, NULL); + channel = silc_idlist_find_channel_by_id(server->global_list, + SILC_ID_GET_ID(id), NULL); if (!channel) { SilcBuffer idp; - if (server->server_type == SILC_ROUTER) + if (server->server_type != SILC_SERVER) goto out; - idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL); - silc_server_send_command(server, server->router->connection, + idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL); + silc_server_send_command(server, SILC_PRIMARY_ROUTE(server), SILC_COMMAND_IDENTIFY, ++server->cmd_ident, - 1, 5, idp->data, idp->len); + 1, 5, idp->data, silc_buffer_len(idp)); silc_buffer_free(idp); /* Register pending command callback. After we've received the channel information we will reprocess this command reply by re-calling this USERS command reply callback. */ - silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, + silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, server->cmd_ident, - NULL, silc_server_command_reply_users, cmd); + silc_server_command_reply_users, cmd); return; } } @@ -969,16 +1321,18 @@ SILC_SERVER_CMD_REPLY_FUNC(users) /* Save the users to the channel */ silc_server_save_users_on_channel(server, cmd->sock, channel, NULL, - client_id_list, client_mode_list, + client_id_list, client_mode_list, list_count); + channel->global_users = silc_server_channel_has_global(channel); + channel->users_resolved = TRUE; + silc_buffer_free(client_id_list); silc_buffer_free(client_mode_list); out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS); - silc_free(channel_id); + err: silc_server_command_reply_free(cmd); } @@ -986,15 +1340,13 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcClientEntry client = NULL; SilcServerEntry server_entry = NULL; - SilcClientID *client_id = NULL; - SilcServerID *server_id = NULL; - SilcSKEPKType type; - unsigned char *tmp, *pk; - uint32 len; - uint16 pk_len; + SilcClientID client_id; + SilcServerID server_id; + unsigned char *tmp; + SilcUInt32 len; SilcIDPayload idp = NULL; SilcIdType id_type; SilcPublicKey public_key = NULL; @@ -1004,7 +1356,7 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) tmp = silc_argument_get_arg_type(cmd->args, 2, &len); if (!tmp) goto out; - idp = silc_id_payload_parse_data(tmp, len); + idp = silc_id_payload_parse(tmp, len); if (!idp) goto out; @@ -1013,57 +1365,155 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) if (!tmp) goto out; - /* Decode the public key */ - - SILC_GET16_MSB(pk_len, tmp); - SILC_GET16_MSB(type, tmp + 2); - pk = tmp + 4; - - if (type != SILC_SKE_PK_TYPE_SILC) - goto out; - - if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) + /* Decode the public key payload */ + if (!silc_public_key_payload_decode(tmp, len, &public_key)) goto out; id_type = silc_id_payload_get_type(idp); if (id_type == SILC_ID_CLIENT) { - client_id = silc_id_payload_get_id(idp); + silc_id_payload_get_id(idp, &client_id, sizeof(client_id)); - client = silc_idlist_find_client_by_id(server->local_list, client_id, + client = silc_idlist_find_client_by_id(server->local_list, &client_id, TRUE, NULL); if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, - client_id, TRUE, NULL); + client = silc_idlist_find_client_by_id(server->global_list, + &client_id, TRUE, NULL); if (!client) goto out; } - client->data.public_key = public_key; + if (!client->data.public_key) { + /* Add client's public key to repository */ + if (!silc_server_get_public_key_by_client(server, client, NULL)) + silc_skr_add_public_key_simple(server->repository, + public_key, + SILC_SKR_USAGE_IDENTIFICATION, + client, NULL); + client->data.public_key = public_key; + public_key = NULL; + } } else if (id_type == SILC_ID_SERVER) { - server_id = silc_id_payload_get_id(idp); + silc_id_payload_get_id(idp, &server_id, sizeof(server_id)); - server_entry = silc_idlist_find_server_by_id(server->local_list, server_id, - TRUE, NULL); + server_entry = silc_idlist_find_server_by_id(server->local_list, + &server_id, TRUE, NULL); if (!server_entry) { - server_entry = silc_idlist_find_server_by_id(server->global_list, - server_id, TRUE, NULL); + server_entry = silc_idlist_find_server_by_id(server->global_list, + &server_id, TRUE, NULL); if (!server_entry) goto out; } server_entry->data.public_key = public_key; + public_key = NULL; } else { goto out; } out: - SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS); + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY); if (idp) silc_id_payload_free(idp); - silc_free(client_id); - silc_free(server_id); if (public_key) silc_pkcs_public_key_free(public_key); + err: + silc_server_command_reply_free(cmd); +} + +SILC_SERVER_CMD_REPLY_FUNC(list) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcStatus status, error; + SilcID id; + SilcChannelEntry channel; + SilcIDCacheEntry cache; + unsigned char *tmp, *name, *namec = NULL, *topic; + SilcUInt32 usercount = 0; + SilcBool global_list = FALSE; + + COMMAND_CHECK_STATUS; + + if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) + goto out; + + name = silc_argument_get_arg_type(cmd->args, 3, NULL); + topic = silc_argument_get_arg_type(cmd->args, 4, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 5, NULL); + if (tmp) + SILC_GET32_MSB(usercount, tmp); + + namec = silc_channel_name_check(name, strlen(name), SILC_STRING_UTF8, + 256, NULL); + if (!namec) + goto out; + + /* Add the channel entry if we do not have it already */ + channel = silc_idlist_find_channel_by_name(server->local_list, + namec, &cache); + if (!channel) { + channel = silc_idlist_find_channel_by_name(server->global_list, + namec, &cache); + global_list = TRUE; + } + if (!channel) { + /* If router did not find such channel in its lists then this must + be bogus channel or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) + goto out; + + channel = silc_idlist_add_channel(server->global_list, strdup(name), + SILC_CHANNEL_MODE_NONE, + silc_id_dup(SILC_ID_GET_ID(id), + SILC_ID_CHANNEL), + server->router, NULL, NULL, NULL); + if (!channel) + goto out; + } + + channel->user_count = usercount; + + if (topic) { + silc_free(channel->topic); + channel->topic = strdup(topic); + } + + /* Pending callbacks are not executed if this was an list entry */ + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_END) { + silc_server_command_reply_free(cmd); + return; + } + + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST); + err: + silc_free(namec); + silc_server_command_reply_free(cmd); +} + +SILC_SERVER_CMD_REPLY_FUNC(watch) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcStatus status, error; + + COMMAND_CHECK_STATUS; + + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WATCH); + err: + silc_server_command_reply_free(cmd); +} + +SILC_SERVER_CMD_REPLY_FUNC(ping) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcStatus status, error; + + COMMAND_CHECK_STATUS; + + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_PING); + err: silc_server_command_reply_free(cmd); }