X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fserver_query.c;h=59eda8024e40cb5d513ef8bc89656311bb9b7150;hp=0ebc05ed779be3929e396773eaf2438bbe36bad2;hb=805fddcf6431e784f9f77114782a90c9d12f9cbe;hpb=f6575ba60e0fd72cc99a84216707b53275f1b1b2 diff --git a/apps/silcd/server_query.c b/apps/silcd/server_query.c index 0ebc05ed..59eda802 100644 --- a/apps/silcd/server_query.c +++ b/apps/silcd/server_query.c @@ -1,10 +1,10 @@ /* - server_query.c + server_query.c Author: Pekka Riikonen - Copyright (C) 2002 Pekka Riikonen + Copyright (C) 2002 - 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 @@ -18,13 +18,11 @@ */ /* $Id$ */ -/* XXX TODO Requested Attributes to WHOIS */ - #include "serverincludes.h" #include "server_internal.h" typedef struct { - SilcSocketConnection sock; /* Connection of this query */ + SilcPacketStream sock; /* Connection of this query */ unsigned char **arg; /* Query argument */ SilcUInt32 *arg_lens; /* Query argument lengths */ SilcUInt32 *arg_types; /* Query argument types */ @@ -44,25 +42,26 @@ typedef struct { typedef struct { void *id; /* ID */ SilcIdType id_type; /* ID type */ - SilcUInt16 index; /* Index to IDs */ - unsigned int from_cmd : 1; /* TRUE if `index' is from command args, - otherwise from query->ids */ + unsigned int index : 15; /* Index to IDs */ + unsigned int type : 2; /* 0 = take from query->ids, 1 = take + from args, 2 = no args in error. */ unsigned int error : 7; /* The actual error (SilcStatus) */ } *SilcServerQueryError; /* Query session context */ typedef struct { /* Queried data */ - char *nickname; /* Queried nickname */ - char *nick_server; /* Queried nickname's server */ - char *server_name; /* Queried server name */ - char *channel_name; /* Queried channel name */ + char nickname[128 + 1]; /* Queried nickname, normalized */ + char nick_server[128 + 1]; /* Queried nickname's server */ + char *server_name; /* Queried server name, normalized */ + char *channel_name; /* Queried channel name, normalized */ SilcServerQueryID ids; /* Queried IDs */ SilcUInt32 ids_count; /* number of queried IDs */ SilcUInt32 reply_count; /* Requested reply count */ SilcDList attrs; /* Requested Attributes in WHOIS */ /* Query session data */ + SilcPacketStream router; /* Router to send our query */ SilcServerCommandContext cmd; /* Command context for query */ SilcServerQueryList querylist; /* Temporary query list context */ SilcServerQueryID queries; /* Ongoing queries */ @@ -71,18 +70,23 @@ typedef struct { SilcUInt16 queries_count; /* Number of ongoing queries */ SilcUInt16 queries_left; /* Number of ongoing queries left */ SilcUInt16 errors_count; /* number of errors */ - unsigned int querycmd : 7; /* Query command (SilcCommand) */ - unsigned int resolved : 1; /* TRUE if normal server has resolved + unsigned int querycmd : 7; /* Query command (SilcCommand) */ + unsigned int resolved : 1; /* TRUE if normal server has resolved information from router */ + unsigned int dynamic_prim : 1; /* Dynamic connection attempt to primary */ + unsigned int dynamic_retry : 1; /* Primary returned error, send to + nick@serv server. */ + unsigned int parsed : 1; /* Set when query is parsed */ } *SilcServerQuery; + void silc_server_query_free(SilcServerQuery query); void silc_server_query_send_error(SilcServer server, SilcServerQuery query, SilcStatus error, ...); void silc_server_query_add_error(SilcServer server, SilcServerQuery query, - bool from_cmd, + SilcUInt32 type, SilcUInt32 index, SilcStatus error); void silc_server_query_add_error_id(SilcServer server, @@ -91,11 +95,12 @@ void silc_server_query_add_error_id(SilcServer server, void *id, SilcIdType id_type); void silc_server_query_send_router(SilcServer server, SilcServerQuery query); void silc_server_query_send_router_reply(void *context, void *reply); -void silc_server_query_parse(SilcServer server, SilcServerQuery query); +SilcBool silc_server_query_parse(SilcServer server, SilcServerQuery query, + SilcBool parse_only); void silc_server_query_process(SilcServer server, SilcServerQuery query, - bool resolve); + SilcBool resolve); void silc_server_query_resolve(SilcServer server, SilcServerQuery query, - SilcSocketConnection sock, + SilcPacketStream sock, SilcClientEntry client_entry); void silc_server_query_resolve_reply(void *context, void *reply); void silc_server_query_send_reply(SilcServer server, @@ -106,9 +111,9 @@ void silc_server_query_send_reply(SilcServer server, SilcUInt32 servers_count, SilcChannelEntry *channels, SilcUInt32 channels_count); -unsigned char *silc_server_query_reply_attrs(SilcServer server, - SilcServerQuery query, - SilcUInt32 *attrs_len); +SilcBuffer silc_server_query_reply_attrs(SilcServer server, + SilcServerQuery query, + SilcClientEntry client_entry); /* Free the query context structure and all allocated resources. */ @@ -122,8 +127,6 @@ void silc_server_query_free(SilcServerQuery query) silc_free(query->queries[i].id); silc_free(query->queries); - silc_free(query->nickname); - silc_free(query->nick_server); silc_free(query->server_name); silc_free(query->channel_name); @@ -165,7 +168,7 @@ void silc_server_query_send_error(SilcServer server, /* Send the command reply with error */ silc_server_send_command_reply(server, query->cmd->sock, - query->querycmd, error, 0, + query->querycmd, error, 0, silc_command_get_ident(query->cmd->payload), argc, data_type, data, data_len); va_end(va); @@ -175,11 +178,13 @@ void silc_server_query_send_error(SilcServer server, processing and this function can be used to add one error. The `index' is the index to the command context which includes the argument which caused the error, or it is the index to query->ids, depending - on value of `from_cmd'. */ + on value of `type'. If `type' is 0 the index is to query->ids, if + it is 1 it is index to the command context arguments, and if it is + 2 the index is ignored and no argument is included in the error. */ void silc_server_query_add_error(SilcServer server, SilcServerQuery query, - bool from_cmd, + SilcUInt32 type, SilcUInt32 index, SilcStatus error) { @@ -188,7 +193,7 @@ void silc_server_query_add_error(SilcServer server, if (!query->errors) return; query->errors[query->errors_count].index = index; - query->errors[query->errors_count].from_cmd = from_cmd; + query->errors[query->errors_count].type = type; query->errors[query->errors_count].error = error; query->errors[query->errors_count].id = NULL; query->errors[query->errors_count].id_type = 0; @@ -208,7 +213,7 @@ void silc_server_query_add_error_id(SilcServer server, if (!query->errors) return; query->errors[query->errors_count].index = 0; - query->errors[query->errors_count].from_cmd = FALSE; + query->errors[query->errors_count].type = 0; query->errors[query->errors_count].error = error; query->errors[query->errors_count].id = silc_id_dup(id, id_type); query->errors[query->errors_count].id_type = id_type; @@ -222,25 +227,37 @@ void silc_server_query_add_error_id(SilcServer server, to the entity who sent this query to us automatically. Returns TRUE if the query is being processed or FALSE on error. */ -bool silc_server_query_command(SilcServer server, SilcCommand querycmd, - SilcServerCommandContext cmd) +SilcBool silc_server_query_command(SilcServer server, + SilcCommand querycmd, + SilcServerCommandContext cmd, + void *old_query) { SilcServerQuery query; SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd))); - query = silc_calloc(1, sizeof(*query)); - query->querycmd = querycmd; - query->cmd = silc_server_command_dup(cmd); + if (!old_query) { + query = silc_calloc(1, sizeof(*query)); + query->querycmd = querycmd; + query->cmd = silc_server_command_dup(cmd); + query->router = SILC_PRIMARY_ROUTE(server); + } else + query = old_query; switch (querycmd) { case SILC_COMMAND_WHOIS: - /* If we are normal server and query contains nickname, send it - directly to router. */ - if (server->server_type == SILC_SERVER && !server->standalone && + /* If we are normal server and query contains nickname OR query + doesn't contain nickname or ids BUT attributes, send it to the + router */ + if (server->server_type != SILC_ROUTER && !server->standalone && cmd->sock != SILC_PRIMARY_ROUTE(server) && - silc_argument_get_arg_type(cmd->args, 1, NULL)) { + (silc_argument_get_arg_type(cmd->args, 1, NULL) || + (!silc_argument_get_arg_type(cmd->args, 1, NULL) && + !silc_argument_get_arg_type(cmd->args, 4, NULL) && + silc_argument_get_arg_type(cmd->args, 3, NULL)))) { + if (!silc_server_query_parse(server, query, TRUE)) + return FALSE; silc_server_query_send_router(server, query); return TRUE; } @@ -262,6 +279,8 @@ bool silc_server_query_command(SilcServer server, SilcCommand querycmd, if (server->server_type == SILC_SERVER && !server->standalone && cmd->sock != SILC_PRIMARY_ROUTE(server) && !silc_argument_get_arg_type(cmd->args, 5, NULL)) { + if (!silc_server_query_parse(server, query, TRUE)) + return FALSE; silc_server_query_send_router(server, query); return TRUE; } @@ -274,11 +293,50 @@ bool silc_server_query_command(SilcServer server, SilcCommand querycmd, } /* Now parse the request */ - silc_server_query_parse(server, query); + silc_server_query_parse(server, query, FALSE); return TRUE; } +/* Remote server connected callback. */ + +void silc_server_query_connected(SilcServer server, + SilcServerEntry server_entry, + void *context) +{ + SilcServerQuery query = context; + + if (!server_entry) { + /* Connecting failed */ + SilcConnectionType type = (server->server_type == SILC_ROUTER ? + SILC_CONN_SERVER : SILC_CONN_ROUTER); + + if (query->dynamic_prim /* && @serv != prim.host.name */ && + !silc_server_num_sockets_by_remote(server, query->nick_server, + query->nick_server, 706, type)) { + /* Connection attempt to primary router failed, now try to the one + specified in nick@server. */ + silc_server_create_connection(server, FALSE, TRUE, query->nick_server, + 706, silc_server_query_connected, + query); + query->dynamic_prim = FALSE; + return; + } + + /* Process the query after failed connect. This will send error back + because such nick was not found. */ + SILC_LOG_DEBUG(("Process query, connecting failed")); + silc_server_query_process(server, query, TRUE); + return; + } + + /* Reprocess the query */ + SILC_LOG_DEBUG(("Reprocess query after creating connection to %s", + server_entry->server_name)); + query->router = server_entry->data.sconn->sock; + silc_server_query_command(server, query->querycmd, query->cmd, query); +} + /* Send the received query to our primary router since we could not handle the query directly. We will reprocess the query after our router replies back. */ @@ -288,16 +346,19 @@ void silc_server_query_send_router(SilcServer server, SilcServerQuery query) SilcBuffer tmpbuf; SilcUInt16 old_ident; - SILC_LOG_DEBUG(("Forwarding the query to router for processing")); + SILC_LOG_DEBUG(("Forwarding the query to router %p for processing", + query->router)); + + /* Statistics */ + server->stat.commands_sent++; /* Send WHOIS command to our router */ old_ident = silc_command_get_ident(query->cmd->payload); silc_command_set_ident(query->cmd->payload, ++server->cmd_ident); tmpbuf = silc_command_payload_encode_payload(query->cmd->payload); - silc_server_packet_send(server, - SILC_PRIMARY_ROUTE(server), + silc_server_packet_send(server, query->router, SILC_PACKET_COMMAND, 0, - tmpbuf->data, tmpbuf->len, TRUE); + tmpbuf->data, silc_buffer_len(tmpbuf)); silc_command_set_ident(query->cmd->payload, old_ident); silc_buffer_free(tmpbuf); @@ -319,66 +380,128 @@ void silc_server_query_send_router_reply(void *context, void *reply) SILC_LOG_DEBUG(("Received reply from router to query")); + /* If the original command caller has gone away, just stop. */ + if (!silc_packet_stream_is_valid(query->cmd->sock)) { + SILC_LOG_DEBUG(("Original command caller vanished")); + silc_server_query_free(query); + return; + } + /* Check if router sent error reply */ if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) { SilcBuffer buffer; + SilcConnectionType type = (server->server_type == SILC_ROUTER ? + SILC_CONN_SERVER : SILC_CONN_ROUTER); + + /* If this was nick@server query, retry to @serv if the primary router + returned error. */ + if (query->nick_server[0] && !query->dynamic_retry && + !silc_server_num_sockets_by_remote(server, query->nick_server, + query->nick_server, 706, type)) { + SILC_LOG_DEBUG(("Retry query by connecting to %s:%d", + query->nick_server, 706)); + silc_server_create_connection(server, FALSE, TRUE, query->nick_server, + 706, silc_server_query_connected, + query); + query->dynamic_retry = TRUE; + query->resolved = FALSE; + return; + } SILC_LOG_DEBUG(("Sending error to original query")); + /* Statistics */ + server->stat.commands_sent++; + /* Send the same command reply payload which contains the error */ silc_command_set_command(cmdr->payload, query->querycmd); silc_command_set_ident(cmdr->payload, silc_command_get_ident(query->cmd->payload)); buffer = silc_command_payload_encode_payload(cmdr->payload); silc_server_packet_send(server, query->cmd->sock, - SILC_PACKET_COMMAND_REPLY, 0, - buffer->data, buffer->len, FALSE); + SILC_PACKET_COMMAND_REPLY, 0, + buffer->data, silc_buffer_len(buffer)); silc_buffer_free(buffer); silc_server_query_free(query); return; } /* Continue with parsing */ - silc_server_query_parse(server, query); + silc_server_query_parse(server, query, FALSE); } /* Parse the command query and start processing the queries in detail. */ -void silc_server_query_parse(SilcServer server, SilcServerQuery query) +SilcBool silc_server_query_parse(SilcServer server, SilcServerQuery query, + SilcBool parse_only) { SilcServerCommandContext cmd = query->cmd; + SilcIDListData idata = silc_packet_get_context(cmd->sock); unsigned char *tmp; SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args); - void *id; - SilcIdType id_type; + SilcID id; int i; SILC_LOG_DEBUG(("Parsing %s query", silc_get_command_name(query->querycmd))); + if (query->parsed) + goto parsed; + switch (query->querycmd) { case SILC_COMMAND_WHOIS: + /* Get requested attributes if set */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); + if (tmp && !query->attrs && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN) { + query->attrs = silc_attribute_payload_parse(tmp, tmp_len); + + /* When Requested Attributes is present we will assure that this + client cannot execute the WHOIS command too fast. This would be + same as having SILC_CF_LAG_STRICT. */ + if (idata && idata->conn_type == SILC_CONN_CLIENT) + ((SilcClientEntry)idata)->fast_command = 6; + } + /* Get Client IDs if present. Take IDs always instead of nickname. */ tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len); if (!tmp) { /* Get nickname */ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len); - if (!tmp) { + if (!tmp && !query->attrs) { + /* No nickname, no ids and no attributes - send error */ silc_server_query_send_error(server, query, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); silc_server_query_free(query); - return; + return FALSE; } /* Get the nickname@server string and parse it */ - if (tmp_len > 128 || - !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) { + if (tmp && ((tmp_len > 128) || + !silc_parse_userfqdn(tmp, query->nickname, + sizeof(query->nickname), + query->nick_server, + sizeof(query->nick_server)))) { silc_server_query_send_error(server, query, SILC_STATUS_ERR_BAD_NICKNAME, 0); silc_server_query_free(query); - return; + return FALSE; + } + + /* Check nickname */ + if (tmp) { + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_server_query_free(query); + return FALSE; + } + memset(query->nickname, 0, sizeof(query->nickname)); + silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp); + silc_free(tmp); } } else { @@ -390,9 +513,9 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) if (!tmp) continue; - id = silc_id_payload_parse_id(tmp, tmp_len, NULL); - if (!id) { - silc_server_query_add_error(server, query, TRUE, i + 4, + if (!silc_id_payload_parse_id(tmp, tmp_len, &id) || + id.type != SILC_ID_CLIENT) { + silc_server_query_add_error(server, query, 1, i + 4, SILC_STATUS_ERR_BAD_CLIENT_ID); continue; } @@ -401,21 +524,23 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) send the query to router, unless done so already */ if (server->server_type == SILC_SERVER && !query->resolved) { if (!silc_idlist_find_client_by_id(server->local_list, - id, TRUE, NULL)) { - if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || + &id.u.client_id, TRUE, NULL)) { + if (idata->conn_type != SILC_CONN_CLIENT || !silc_idlist_find_client_by_id(server->global_list, - id, TRUE, NULL)) { + &id.u.client_id, TRUE, NULL)) { silc_server_query_send_router(server, query); for (i = 0; i < query->ids_count; i++) silc_free(query->ids[i].id); silc_free(query->ids); - silc_free(id); - return; + query->ids = NULL; + query->ids_count = 0; + return FALSE; } } } - query->ids[query->ids_count].id = id; + query->ids[query->ids_count].id = silc_id_dup(&id.u.client_id, + SILC_ID_CLIENT); query->ids[query->ids_count].id_type = SILC_ID_CLIENT; query->ids_count++; } @@ -425,12 +550,7 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (tmp && tmp_len == sizeof(SilcUInt32)) SILC_GET32_MSB(query->reply_count, tmp); - - /* Get requested attributes if set */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); - if (tmp) - query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len); - break; + break; case SILC_COMMAND_WHOWAS: /* Get nickname */ @@ -439,18 +559,32 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) silc_server_query_send_error(server, query, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); silc_server_query_free(query); - return; + return FALSE; } /* Get the nickname@server string and parse it */ if (tmp_len > 128 || - !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) { + !silc_parse_userfqdn(tmp, query->nickname, sizeof(query->nickname), + query->nick_server, sizeof(query->nick_server))) { silc_server_query_send_error(server, query, SILC_STATUS_ERR_BAD_NICKNAME, 0); silc_server_query_free(query); - return; + return FALSE; } + /* Check nickname */ + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_server_query_free(query); + return FALSE; + } + memset(query->nickname, 0, sizeof(query->nickname)); + silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp); + silc_free(tmp); + /* Get the max count of reply messages allowed */ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (tmp && tmp_len == sizeof(SilcUInt32)) @@ -467,26 +601,62 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) if (tmp) { /* Get the nickname@server string and parse it */ if (tmp_len > 128 || - !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) - silc_server_query_add_error(server, query, TRUE, 1, + !silc_parse_userfqdn(tmp, query->nickname, + sizeof(query->nickname), + query->nick_server, + sizeof(query->nick_server))) + silc_server_query_add_error(server, query, 1, 1, SILC_STATUS_ERR_BAD_NICKNAME); + + /* Check nickname */ + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_server_query_free(query); + return FALSE; + } + memset(query->nickname, 0, sizeof(query->nickname)); + silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp); + silc_free(tmp); } /* Try get server name */ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); - if (tmp) - query->server_name = silc_memdup(tmp, tmp_len); + if (tmp) { + /* Check server name */ + tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, + 256, &tmp_len); + if (!tmp) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_SERVER, 0); + silc_server_query_free(query); + return FALSE; + } + query->server_name = tmp; + } /* Get channel name */ tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); - if (tmp && tmp_len <= 256) - query->channel_name = silc_memdup(tmp, tmp_len); + if (tmp && tmp_len <= 256) { + /* Check channel name */ + tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, + 256, &tmp_len); + if (!tmp) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_CHANNEL, 0); + silc_server_query_free(query); + return FALSE; + } + query->channel_name = tmp; + } - if (!query->nickname && !query->server_name && !query->channel_name) { + if (!query->nickname[0] && !query->server_name && !query->channel_name) { silc_server_query_send_error(server, query, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); silc_server_query_free(query); - return; + return FALSE; } } else { @@ -498,9 +668,8 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) if (!tmp) continue; - id = silc_id_payload_parse_id(tmp, tmp_len, &id_type); - if (!id) { - silc_server_query_add_error(server, query, TRUE, i + 5, + if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) { + silc_server_query_add_error(server, query, 1, i + 5, SILC_STATUS_ERR_BAD_CLIENT_ID); continue; } @@ -508,23 +677,45 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) /* Normal server must check whether this ID exist, and if not then send the query to router, unless done so already */ if (server->server_type == SILC_SERVER && !query->resolved) { - if (!silc_idlist_find_client_by_id(server->local_list, - id, TRUE, NULL)) { - if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || - !silc_idlist_find_client_by_id(server->global_list, - id, TRUE, NULL)) { - silc_server_query_send_router(server, query); - for (i = 0; i < query->ids_count; i++) - silc_free(query->ids[i].id); - silc_free(query->ids); - silc_free(id); - return; + if (id.type == SILC_ID_CLIENT) { + if (!silc_idlist_find_client_by_id(server->local_list, + &id.u.client_id, TRUE, NULL)) { + if (idata->conn_type != SILC_CONN_CLIENT || + !silc_idlist_find_client_by_id(server->global_list, + &id.u.client_id, TRUE, + NULL)) { + silc_server_query_send_router(server, query); + for (i = 0; i < query->ids_count; i++) + silc_free(query->ids[i].id); + silc_free(query->ids); + query->ids = NULL; + query->ids_count = 0; + return FALSE; + } } + } else { + /* For now all other ID's except Client ID's are explicitly + sent to router for resolving. */ + silc_server_query_send_router(server, query); + for (i = 0; i < query->ids_count; i++) + silc_free(query->ids[i].id); + silc_free(query->ids); + query->ids = NULL; + query->ids_count = 0; + return FALSE; } } - query->ids[query->ids_count].id = id; - query->ids[query->ids_count].id_type = id_type; + if (id.type == SILC_ID_CLIENT) + query->ids[query->ids_count].id = silc_id_dup(&id.u.client_id, + SILC_ID_CLIENT); + if (id.type == SILC_ID_SERVER) + query->ids[query->ids_count].id = silc_id_dup(&id.u.server_id, + SILC_ID_SERVER); + if (id.type == SILC_ID_CHANNEL) + query->ids[query->ids_count].id = silc_id_dup(&id.u.channel_id, + SILC_ID_CHANNEL); + query->ids[query->ids_count].id_type = id.type; query->ids_count++; } } @@ -536,8 +727,197 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) break; } + query->parsed = TRUE; + + parsed: + if (!parse_only && query->nickname) { + switch (query->querycmd) { + case SILC_COMMAND_WHOIS: + case SILC_COMMAND_IDENTIFY: + /* Check server name. If we are open server and don't yet have + connection to remote router, create it now. */ + if (query->nick_server[0] && server->config->dynamic_server && + !query->resolved) { + /* If primary router is specified, use that. Otherwise connect + to the server in nick@server string. */ + SilcServerConfigRouter *router; + SilcConnectionType type = (server->server_type == SILC_ROUTER ? + SILC_CONN_SERVER : SILC_CONN_ROUTER); + + router = silc_server_config_get_primary_router(server); + if (router && server->standalone) { + /* Create connection to primary router */ + SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d", + router->host, router->port)); + query->dynamic_prim = TRUE; + silc_server_create_connection(server, FALSE, TRUE, + router->host, router->port, + silc_server_query_connected, query); + return FALSE; + } else if (!silc_server_num_sockets_by_remote(server, + query->nick_server, + query->nick_server, + 706, type)) { + /* Create connection and handle the query after connection */ + SILC_LOG_DEBUG(("Create dynamic connection to %s:%d", + query->nick_server, 706)); + silc_server_create_connection(server, FALSE, TRUE, + query->nick_server, 706, + silc_server_query_connected, query); + return FALSE; + } + } + } + } + /* Start processing the query information */ - silc_server_query_process(server, query, TRUE); + if (!parse_only) + silc_server_query_process(server, query, TRUE); + + return TRUE; +} + +/* Context for holding clients searched by public key. */ +typedef struct { + SilcClientEntry **clients; + SilcUInt32 *clients_count; + SilcBool found; +} *SilcServerPublicKeyUser, SilcServerPublicKeyUserStruct; + +/* SKR find callbcak */ + +static void silc_server_query_skr_callback(SilcSKR skr, + SilcSKRFind find, + SilcSKRStatus status, + SilcDList keys, + void *context) +{ + SilcServerPublicKeyUser uc = context; + SilcSKRKey key; + + if (keys) { + (*uc->clients) = silc_realloc((*uc->clients), + sizeof((**uc->clients)) * + ((*uc->clients_count) + + silc_dlist_count(keys))); + + silc_dlist_start(keys); + while ((key = silc_dlist_get(keys))) + (*uc->clients)[(*uc->clients_count)++] = key->key_context; + + uc->found = TRUE; + silc_dlist_uninit(keys); + } + + silc_skr_find_free(find); +} + +/* If clients are set, limit the found clients using the attributes in + the query. If clients are not set, try to find some clients using + the attributes */ + +void silc_server_query_check_attributes(SilcServer server, + SilcServerQuery query, + SilcClientEntry **clients, + SilcUInt32 *clients_count) { + SilcClientEntry entry; + SilcAttributePayload attr; + SilcAttribute attribute; + SilcAttributeObjPk pk; + SilcPublicKey publickey, cmp_pubkey; + SilcPKCSType type; + SilcBool found = FALSE, no_clients = FALSE, search_pubkey = FALSE; + int i; + + /* If no clients were found, we only check the attributes + if the user wasn't searching for nickname/ids */ + if (!(*clients)) { + no_clients = TRUE; + if (query->nickname[0] || query->ids_count) + return; + } + + silc_dlist_start(query->attrs); + while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) { + attribute = silc_attribute_get_attribute(attr); + switch (attribute) { + + case SILC_ATTRIBUTE_USER_PUBLIC_KEY: + SILC_LOG_DEBUG(("Finding clients by public key attribute")); + + if (!silc_attribute_get_object(attr, &pk, sizeof(pk))) + continue; + + if (!strcmp(pk.type, "silc-rsa")) + type = SILC_PKCS_SILC; + else if (!strcmp(pk.type, "ssh-rsa")) + type = SILC_PKCS_SSH2; + else if (!strcmp(pk.type, "x509v3-sign-rsa")) + type = SILC_PKCS_X509V3; + else if (!strcmp(pk.type, "pgp-sign-rsa")) + type = SILC_PKCS_OPENPGP; + else + continue; + + if (!silc_pkcs_public_key_alloc(type, pk.data, pk.data_len, + &publickey)) { + silc_free(pk.type); + silc_free(pk.data); + continue; + } + search_pubkey = TRUE; + + /* If no clients were set on calling this function, we just search + for clients, otherwise we try to limit the clients. */ + if (no_clients) { + SilcServerPublicKeyUserStruct usercontext; + SilcSKRFind find; + + usercontext.clients = clients; + usercontext.clients_count = clients_count; + usercontext.found = FALSE; + + find = silc_skr_find_alloc(); + if (!find) + continue; + + silc_skr_find_set_public_key(find, publickey); + silc_skr_find_set_usage(find, SILC_SKR_USAGE_IDENTIFICATION); + silc_skr_find(server->repository, server->schedule, + find, silc_server_query_skr_callback, &usercontext); + + if (usercontext.found == TRUE) + found = TRUE; + } else { + for (i = 0; i < *clients_count; i++) { + entry = (*clients)[i]; + + if (!entry->data.public_key) + continue; + + if (silc_server_get_public_key_by_client(server, entry, + &cmp_pubkey)) { + if (silc_pkcs_public_key_compare(cmp_pubkey, publickey)) { + found = TRUE; + continue; + } + } + + (*clients)[i] = NULL; + } + } + silc_free(pk.type); + silc_free(pk.data); + silc_pkcs_public_key_free(publickey); + break; + } + } + + if (!found && !query->nickname[0] && !query->ids) + silc_server_query_add_error(server, query, 2, 0, + search_pubkey ? + SILC_STATUS_ERR_NO_SUCH_PUBLIC_KEY : + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); } /* Processes the parsed query. This does the actual finding of the @@ -545,10 +925,11 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) sender of the query command. */ void silc_server_query_process(SilcServer server, SilcServerQuery query, - bool resolve) + SilcBool resolve) { SilcServerCommandContext cmd = query->cmd; - bool check_global = FALSE; + SilcIDListData idata = silc_packet_get_context(cmd->sock); + SilcBool check_global = FALSE; void *entry; SilcClientEntry *clients = NULL, client_entry; SilcChannelEntry *channels = NULL; @@ -561,34 +942,42 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, /* Check global lists if query is coming from client or we are not normal server (we know global information). */ - if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) + if (idata->conn_type == SILC_CONN_CLIENT) check_global = TRUE; else if (server->server_type != SILC_SERVER) check_global = TRUE; - if (query->nickname) { + if (query->nickname[0]) { /* Get all clients matching nickname from local list */ - if (!silc_idlist_get_clients_by_hash(server->local_list, - query->nickname, server->md5hash, + if (!silc_idlist_get_clients_by_hash(server->local_list, + query->nickname, + query->nick_server[0] ? + query->nick_server : NULL, + server->md5hash, &clients, &clients_count)) - silc_idlist_get_clients_by_nickname(server->local_list, + silc_idlist_get_clients_by_nickname(server->local_list, query->nickname, - query->nick_server, + query->nick_server[0] ? + query->nick_server : NULL, &clients, &clients_count); /* Check global list as well */ if (check_global) { - if (!silc_idlist_get_clients_by_hash(server->global_list, - query->nickname, server->md5hash, + if (!silc_idlist_get_clients_by_hash(server->global_list, + query->nickname, + query->nick_server[0] ? + query->nick_server : NULL, + server->md5hash, &clients, &clients_count)) - silc_idlist_get_clients_by_nickname(server->global_list, + silc_idlist_get_clients_by_nickname(server->global_list, query->nickname, - query->nick_server, + query->nick_server[0] ? + query->nick_server : NULL, &clients, &clients_count); } if (!clients) - silc_server_query_add_error(server, query, TRUE, 1, + silc_server_query_add_error(server, query, 1, 1, SILC_STATUS_ERR_NO_SUCH_NICK); } @@ -605,7 +994,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, } if (!servers) - silc_server_query_add_error(server, query, TRUE, 2, + silc_server_query_add_error(server, query, 1, 2, SILC_STATUS_ERR_NO_SUCH_SERVER); } @@ -623,7 +1012,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, } if (!channels) - silc_server_query_add_error(server, query, TRUE, 3, + silc_server_query_add_error(server, query, 1, 3, SILC_STATUS_ERR_NO_SUCH_CHANNEL); } @@ -644,7 +1033,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, entry = silc_idlist_find_client_by_id(server->global_list, id, TRUE, NULL); if (!entry) { - silc_server_query_add_error(server, query, FALSE, i, + silc_server_query_add_error(server, query, 0, i, SILC_STATUS_ERR_NO_SUCH_CLIENT_ID); continue; } @@ -662,7 +1051,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, entry = silc_idlist_find_server_by_id(server->global_list, id, TRUE, NULL); if (!entry) { - silc_server_query_add_error(server, query, FALSE, i, + silc_server_query_add_error(server, query, 0, i, SILC_STATUS_ERR_NO_SUCH_SERVER_ID); continue; } @@ -679,7 +1068,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, entry = silc_idlist_find_channel_by_id(server->global_list, id, NULL); if (!entry) { - silc_server_query_add_error(server, query, FALSE, i, + silc_server_query_add_error(server, query, 0, i, SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID); continue; } @@ -695,6 +1084,15 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, } } + /* Check the attributes to narrow down the search by using them. */ + if (query->attrs) + silc_server_query_check_attributes(server, query, &clients, + &clients_count); + + SILC_LOG_DEBUG(("Querying %d clients", clients_count)); + SILC_LOG_DEBUG(("Querying %d servers", servers_count)); + SILC_LOG_DEBUG(("Querying %d channels", channels_count)); + /* If nothing was found, then just send the errors */ if (!clients && !channels && !servers) { silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0); @@ -748,6 +1146,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, } } + /* Remove the NOATTR status periodically */ + if (client_entry->data.status & SILC_IDLIST_STATUS_NOATTR && + client_entry->updated + 600 < time(NULL)) + client_entry->data.status &= ~SILC_IDLIST_STATUS_NOATTR; + /* When requested attributes is present and local client is detached we cannot send the command to the client, we'll reply on behalf of the client instead. */ @@ -756,6 +1159,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, client_entry->data.status & SILC_IDLIST_STATUS_NOATTR)) continue; + /* If attributes are present in query, and in the entry and we have + done resolvings already we don't need to resolve anymore */ + if (query->resolved && query->attrs && client_entry->attrs) + continue; + /* Resolve the detailed client information. If client is local we know that attributes were present and we will resolve directly from the client. Otherwise resolve from client's owner. */ @@ -835,7 +1243,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, client entry calls this function to do the resolving. */ void silc_server_query_resolve(SilcServer server, SilcServerQuery query, - SilcSocketConnection sock, + SilcPacketStream sock, SilcClientEntry client_entry) { SilcServerCommandContext cmd = query->cmd; @@ -864,12 +1272,30 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query, for (i = 0; i < query->querylist_count; i++) { r = &query->querylist[i]; + /* If Requested Attributes were present put them to this resolving */ + if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) { + len = r->argc + 1; + r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len); + r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len); + r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len); + + tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + if (tmp) + r->arg[r->argc] = silc_memdup(tmp, len); + r->arg_lens[r->argc] = len; + r->arg_types[r->argc] = 3; + r->argc++; + } + + /* Statistics */ + server->stat.commands_sent++; + /* Send WHOIS command */ res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS, r->argc, r->arg, r->arg_lens, r->arg_types, r->ident); silc_server_packet_send(server, r->sock, SILC_PACKET_COMMAND, 0, - res_cmd->data, res_cmd->len, FALSE); + res_cmd->data, silc_buffer_len(res_cmd)); silc_buffer_free(res_cmd); /* Reprocess this packet after received reply */ @@ -913,7 +1339,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query, case SILC_COMMAND_WHOIS: case SILC_COMMAND_IDENTIFY: /* Take existing query context if exist for this connection */ - for (i = 0; i < query->queries_count; i++) + for (i = 0; i < query->querylist_count; i++) if (query->querylist[i].sock == sock) { r = &query->querylist[i]; break; @@ -922,7 +1348,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query, if (!r) { /* Allocate new temp query list context */ query->querylist = silc_realloc(query->querylist, - sizeof(*query->querylist) * + sizeof(*query->querylist) * (query->querylist_count + 1)); r = &query->querylist[query->querylist_count]; query->querylist_count++; @@ -933,21 +1359,6 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query, r->timeout = 3; } - /* If Requested Attributes were present put them to this resolving */ - if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) { - len = r->argc + 1; - r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len); - r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len); - r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len); - - tmp = silc_argument_get_arg_type(cmd->args, 3, &len); - if (tmp) - r->arg[r->argc] = silc_memdup(tmp, len); - r->arg_lens[r->argc] = len; - r->arg_types[r->argc] = 3; - r->argc++; - } - len = r->argc + 1; r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len); r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len); @@ -955,8 +1366,8 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query, /* Add the client entry to be resolved */ idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT); - r->arg[r->argc] = silc_memdup(idp->data, idp->len); - r->arg_lens[r->argc] = idp->len; + r->arg[r->argc] = silc_memdup(idp->data, silc_buffer_len(idp)); + r->arg_lens[r->argc] = silc_buffer_len(idp); r->arg_types[r->argc] = r->argc + 4; r->argc++; silc_buffer_free(idp); @@ -979,6 +1390,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query, client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING; client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED; client_entry->resolve_cmd_ident = ident; + client_entry->updated = time(NULL); /* Save the queried ID, which we will reprocess after we get this and all other queries back. */ @@ -1066,6 +1478,13 @@ void silc_server_query_resolve_reply(void *context, void *reply) SILC_LOG_DEBUG(("Reprocess the query")); + /* If the original command caller has gone away, just stop. */ + if (!silc_packet_stream_is_valid(query->cmd->sock)) { + SILC_LOG_DEBUG(("Original command caller vanished")); + silc_server_query_free(query); + return; + } + /* We have received all queries. Now re-search all information required to complete this query. Reason we cannot save the values found in the first search is that SilcClientEntry, SilcServerEntry and @@ -1093,27 +1512,36 @@ void silc_server_query_send_reply(SilcServer server, SilcUInt32 channels_count) { SilcServerCommandContext cmd = query->cmd; + SilcIDListData idata = silc_packet_get_context(cmd->sock); SilcUInt16 ident = silc_command_get_ident(cmd->payload); SilcStatus status; unsigned char *tmp; + char *tmp2; SilcUInt32 len; SilcBuffer idp; int i, k, valid_count; - char nh[256], uh[256]; - bool sent_reply = FALSE; + char nh[384], uh[384]; + SilcBool sent_reply = FALSE; SILC_LOG_DEBUG(("Sending reply to query")); + SILC_LOG_DEBUG(("Sending %d clients", clients_count)); + SILC_LOG_DEBUG(("Sending %d servers", servers_count)); + SILC_LOG_DEBUG(("Sending %d channels", channels_count)); + SILC_LOG_DEBUG(("Sending %d errors", query->errors_count)); status = SILC_STATUS_OK; /* Send clients */ if (clients_count) { SilcClientEntry entry; - SilcSocketConnection hsock; + SilcPacketStream hsock; /* Mark all invalid entries */ for (i = 0, valid_count = 0; i < clients_count; i++) { entry = clients[i]; + if (!entry) + continue; + switch (query->querycmd) { case SILC_COMMAND_WHOIS: if (!entry->nickname || !entry->username || !entry->userinfo || @@ -1166,7 +1594,7 @@ void silc_server_query_send_reply(SilcServer server, if (k >= 1) status = SILC_STATUS_LIST_ITEM; - if (valid_count > 1 && k == valid_count - 1 + if (valid_count > 1 && k == valid_count - 1 && !servers_count && !channels_count && !query->errors_count) status = SILC_STATUS_LIST_END; if (query->reply_count && k - 1 == query->reply_count) @@ -1180,7 +1608,6 @@ void silc_server_query_send_reply(SilcServer server, " : "), entry->nickname)); idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); - memset(uh, 0, sizeof(uh)); memset(nh, 0, sizeof(nh)); silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname)); @@ -1197,33 +1624,36 @@ void silc_server_query_send_reply(SilcServer server, server->server_name, len); } } - + switch (query->querycmd) { - + case SILC_COMMAND_WHOIS: { unsigned char idle[4], mode[4]; unsigned char *fingerprint, fempty[20], *attrs = NULL; - SilcBuffer channels, umode_list = NULL; + SilcBuffer channels, umode_list = NULL, tmpattrs = NULL; memset(fempty, 0, sizeof(fempty)); memset(idle, 0, sizeof(idle)); + memset(uh, 0, sizeof(uh)); + silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username)); if (!strchr(entry->username, '@') && entry->connection) { hsock = entry->connection; silc_strncat(uh, sizeof(uh), "@", 1); - len = strlen(hsock->hostname); - silc_strncat(uh, sizeof(uh), hsock->hostname, len); + silc_socket_stream_get_info(silc_packet_stream_get_stream(hsock), + NULL, (const char **)&tmp2, NULL, NULL); + silc_strncat(uh, sizeof(uh), tmp2, strlen(tmp2)); } - if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) + if (idata->conn_type == SILC_CONN_CLIENT) channels = - silc_server_get_client_channel_list(server, entry, FALSE, + silc_server_get_client_channel_list(server, entry, FALSE, FALSE, &umode_list); else channels = - silc_server_get_client_channel_list(server, entry, TRUE, + silc_server_get_client_channel_list(server, entry, TRUE, TRUE, &umode_list); if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty))) @@ -1239,37 +1669,46 @@ void silc_server_query_send_reply(SilcServer server, attributes we will reply to them on behalf of the client. */ len = 0; if (query->attrs) { - if (!entry->attrs) { - attrs = silc_server_query_reply_attrs(server, query, &len); - } else { - attrs = entry->attrs; - len = entry->attrs_len; + if (!entry->attrs && SILC_IS_LOCAL(entry)) { + tmpattrs = silc_server_query_reply_attrs(server, query, entry); + entry->attrs = silc_buffer_steal(tmpattrs, &len); + entry->attrs_len = len; + silc_buffer_free(tmpattrs); } + attrs = entry->attrs; + len = entry->attrs_len; } /* Send command reply */ silc_server_send_command_reply(server, cmd->sock, query->querycmd, status, 0, ident, 10, - 2, idp->data, idp->len, + 2, idp->data, silc_buffer_len(idp), 3, nh, strlen(nh), 4, uh, strlen(uh), - 5, entry->userinfo, + 5, entry->userinfo, strlen(entry->userinfo), 6, channels ? channels->data : NULL, - channels ? channels->len : 0, + channels ? silc_buffer_len(channels) + : 0, 7, mode, 4, 8, idle, 4, 9, fingerprint, fingerprint ? 20 : 0, 10, umode_list ? umode_list->data : - NULL, umode_list ? umode_list->len : + NULL, umode_list ? + silc_buffer_len(umode_list) : 0, 11, attrs, len); sent_reply = TRUE; - /* For now we will delete Requested Attributes */ - silc_free(entry->attrs); - entry->attrs = NULL; + /* For now we always delete Requested Attributes, unless the client + is detached, in which case we don't want to reconstruct the + same data everytime */ + if (!(entry->mode & SILC_UMODE_DETACHED) && + !(entry->data.status & SILC_IDLIST_STATUS_NOATTR)) { + silc_free(entry->attrs); + entry->attrs = NULL; + } if (channels) silc_buffer_free(channels); @@ -1284,22 +1723,25 @@ void silc_server_query_send_reply(SilcServer server, if (!entry->username) { silc_server_send_command_reply(server, cmd->sock, query->querycmd, status, 0, ident, 2, - 2, idp->data, idp->len, + 2, idp->data, silc_buffer_len(idp), 3, nh, strlen(nh)); sent_reply = TRUE; } else { + memset(uh, 0, sizeof(uh)); silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username)); if (!strchr(entry->username, '@') && entry->connection) { hsock = entry->connection; silc_strncat(uh, sizeof(uh), "@", 1); - len = strlen(hsock->hostname); - silc_strncat(uh, sizeof(uh), hsock->hostname, len); + silc_socket_stream_get_info(silc_packet_stream_get_stream(hsock), + NULL, (const char **)&tmp2, + NULL, NULL); + silc_strncat(uh, sizeof(uh), tmp2, strlen(tmp2)); } silc_server_send_command_reply(server, cmd->sock, query->querycmd, status, 0, ident, 3, - 2, idp->data, idp->len, + 2, idp->data, silc_buffer_len(idp), 3, nh, strlen(nh), 4, uh, strlen(uh)); sent_reply = TRUE; @@ -1307,18 +1749,19 @@ void silc_server_query_send_reply(SilcServer server, break; case SILC_COMMAND_WHOWAS: + memset(uh, 0, sizeof(uh)); silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username)); if (!strchr(entry->username, '@')) - silc_strncat(uh, sizeof(uh), "@*private*", 10); + silc_strncat(uh, sizeof(uh), "@-private-", 10); /* Send command reply */ silc_server_send_command_reply(server, cmd->sock, query->querycmd, status, 0, ident, 4, - 2, idp->data, idp->len, + 2, idp->data, silc_buffer_len(idp), 3, nh, strlen(nh), 4, uh, strlen(uh), - 5, entry->userinfo, - entry->userinfo ? + 5, entry->userinfo, + entry->userinfo ? strlen(entry->userinfo) : 0); sent_reply = TRUE; break; @@ -1335,9 +1778,14 @@ void silc_server_query_send_reply(SilcServer server, /* Not one valid entry was found, send error. If nickname was used in query send error based on that, otherwise the query->errors already includes proper errors. */ - if (query->nickname) - silc_server_query_add_error(server, query, TRUE, 1, + if (query->nickname[0] || (!query->ids && query->attrs)) + silc_server_query_add_error(server, query, 1, 1, SILC_STATUS_ERR_NO_SUCH_NICK); + + /* Make sure some error is sent */ + if (!query->errors_count && !servers_count && !channels_count) + silc_server_query_add_error(server, query, 2, 0, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); } } @@ -1351,15 +1799,18 @@ void silc_server_query_send_reply(SilcServer server, k = 0; for (i = 0; i < servers_count; i++) { entry = servers[i]; - + if (k >= 1) status = SILC_STATUS_LIST_ITEM; + if (servers_count == 1 && status != SILC_STATUS_OK && !channels_count && + !query->errors_count) + status = SILC_STATUS_LIST_END; if (servers_count > 1 && k == servers_count - 1 && !channels_count && !query->errors_count) status = SILC_STATUS_LIST_END; if (query->reply_count && k - 1 == query->reply_count) status = SILC_STATUS_LIST_END; - + SILC_LOG_DEBUG(("%s: server %s", (status == SILC_STATUS_OK ? " OK" : status == SILC_STATUS_LIST_START ? "START" : @@ -1372,13 +1823,13 @@ void silc_server_query_send_reply(SilcServer server, idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER); silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY, status, 0, ident, 2, - 2, idp->data, idp->len, - 3, entry->server_name, - entry->server_name ? + 2, idp->data, silc_buffer_len(idp), + 3, entry->server_name, + entry->server_name ? strlen(entry->server_name) : 0); silc_buffer_free(idp); sent_reply = TRUE; - + if (status == SILC_STATUS_LIST_END) break; k++; @@ -1395,15 +1846,18 @@ void silc_server_query_send_reply(SilcServer server, k = 0; for (i = 0; i < channels_count; i++) { entry = channels[i]; - + if (k >= 1) status = SILC_STATUS_LIST_ITEM; + if (channels_count == 1 && status != SILC_STATUS_OK && + !query->errors_count) + status = SILC_STATUS_LIST_END; if (channels_count > 1 && k == channels_count - 1 && !query->errors_count) status = SILC_STATUS_LIST_END; if (query->reply_count && k - 1 == query->reply_count) status = SILC_STATUS_LIST_END; - + SILC_LOG_DEBUG(("%s: channel %s", (status == SILC_STATUS_OK ? " OK" : status == SILC_STATUS_LIST_START ? "START" : @@ -1416,13 +1870,13 @@ void silc_server_query_send_reply(SilcServer server, idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL); silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY, status, 0, ident, 2, - 2, idp->data, idp->len, - 3, entry->channel_name, - entry->channel_name ? + 2, idp->data, silc_buffer_len(idp), + 3, entry->channel_name, + entry->channel_name ? strlen(entry->channel_name) : 0); silc_buffer_free(idp); sent_reply = TRUE; - + if (status == SILC_STATUS_LIST_END) break; k++; @@ -1441,31 +1895,38 @@ void silc_server_query_send_reply(SilcServer server, idp = NULL; /* Take error argument */ - if (query->errors[i].from_cmd) { + if (query->errors[i].type == 1) { + /* Take from sent arguments */ len = 0; tmp = silc_argument_get_arg_type(cmd->args, query->errors[i].index, &len); - if (query->errors[i].index == 1) - type = 3; /* Nickname */ - else - type = 2; /* ID */ + type = 2; + } else if (query->errors[i].type == 2) { + /* No argument */ + len = 0; + tmp = NULL; + type = 0; } else if (!query->errors[i].id) { + /* Take from query->ids */ idp = silc_id_payload_encode(query->ids[query->errors[i].index].id, query->ids[query->errors[k].index].id_type); tmp = idp->data; - len = idp->len; + len = silc_buffer_len(idp); type = 2; } else { + /* Take added ID. */ idp = silc_id_payload_encode(query->errors[i].id, query->errors[k].id_type); tmp = idp->data; - len = idp->len; + len = silc_buffer_len(idp); type = 2; } if (k >= 1) status = SILC_STATUS_LIST_ITEM; + if (query->errors_count == 1 && status != SILC_STATUS_OK) + status = SILC_STATUS_LIST_END; if (query->errors_count > 1 && k == query->errors_count - 1) status = SILC_STATUS_LIST_END; if (query->reply_count && k - 1 == query->reply_count) @@ -1476,10 +1937,22 @@ void silc_server_query_send_reply(SilcServer server, status == SILC_STATUS_LIST_START ? "START" : status == SILC_STATUS_LIST_ITEM ? " ITEM" : status == SILC_STATUS_LIST_END ? " END" : - " : "), + " : "), silc_get_status_message(query->errors[i].error), query->errors[i].error)); +#if 1 /* XXX Backwards compatibility. Remove in 1.0. */ + if (query->errors[i].error == SILC_STATUS_ERR_NO_SUCH_NICK) + /* Send error */ + silc_server_send_command_reply(server, cmd->sock, query->querycmd, + (status == SILC_STATUS_OK ? + query->errors[i].error : status), + (status == SILC_STATUS_OK ? + 0 : query->errors[i].error), ident, 2, + type, tmp, len, + 3, tmp, len); + else +#endif /* Send error */ silc_server_send_command_reply(server, cmd->sock, query->querycmd, (status == SILC_STATUS_OK ? @@ -1487,6 +1960,7 @@ void silc_server_query_send_reply(SilcServer server, (status == SILC_STATUS_OK ? 0 : query->errors[i].error), ident, 1, type, tmp, len); + silc_buffer_free(idp); sent_reply = TRUE; @@ -1508,19 +1982,152 @@ void silc_server_query_send_reply(SilcServer server, Either client does not support Requested Attributes or isn't replying to them like it should. */ -unsigned char *silc_server_query_reply_attrs(SilcServer server, - SilcServerQuery query, - SilcUInt32 *attrs_len) +SilcBuffer silc_server_query_reply_attrs(SilcServer server, + SilcServerQuery query, + SilcClientEntry client_entry) { - unsigned char *attrs = NULL; - SilcUInt32 len = 0; + SilcBuffer buffer = NULL; + SilcAttribute attribute; + SilcAttributePayload attr; + SilcAttributeObjPk pk; + SilcAttributeObjService service; + unsigned char *tmp; + unsigned char sign[2048 + 1]; + SilcUInt32 sign_len; SILC_LOG_DEBUG(("Constructing Requested Attributes")); - if (attrs_len) - *attrs_len = len; + /* Go through all requested attributes */ + silc_dlist_start(query->attrs); + while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) { + attribute = silc_attribute_get_attribute(attr); + switch (attribute) { + + case SILC_ATTRIBUTE_SERVICE: + /* Put SERVICE. Put only SILC service. */ + memset(&service, 0, sizeof(service)); + service.port = (server->config->server_info->primary ? + server->config->server_info->primary->port : SILC_PORT); + silc_strncat(service.address, sizeof(service.address), + server->server_name, strlen(server->server_name)); + service.status = !(client_entry->mode & SILC_UMODE_DETACHED); + if (client_entry->connection) + service.idle = time(NULL) - client_entry->data.last_receive; + buffer = silc_attribute_payload_encode(buffer, attribute, + SILC_ATTRIBUTE_FLAG_VALID, + &service, sizeof(service)); + if (!buffer) + return NULL; + break; + + case SILC_ATTRIBUTE_STATUS_MOOD: + /* Put STATUS_MOOD */ + buffer = silc_attribute_payload_encode(buffer, attribute, + SILC_ATTRIBUTE_FLAG_VALID, + (void *) + SILC_ATTRIBUTE_MOOD_NORMAL, + sizeof(SilcUInt32)); + if (!buffer) + return NULL; + break; + + case SILC_ATTRIBUTE_STATUS_FREETEXT: + /* Put STATUS_FREETEXT. We just tell in the message that we are + replying on behalf of the client. */ + tmp = + "This information was provided by the server on behalf of the user"; + buffer = silc_attribute_payload_encode(buffer, attribute, + SILC_ATTRIBUTE_FLAG_VALID, + tmp, strlen(tmp)); + if (!buffer) + return NULL; + break; + + case SILC_ATTRIBUTE_PREFERRED_CONTACT: + /* Put PREFERRED_CONTACT */ + buffer = silc_attribute_payload_encode(buffer, attribute, + SILC_ATTRIBUTE_FLAG_VALID, + (void *) + SILC_ATTRIBUTE_CONTACT_CHAT, + sizeof(SilcUInt32)); + if (!buffer) + return NULL; + break; + + case SILC_ATTRIBUTE_USER_PUBLIC_KEY: + /* Put USER_PUBLIC_KEY */ + if (client_entry->data.public_key) { + pk.type = "silc-rsa"; + pk.data = silc_pkcs_public_key_encode(client_entry->data.public_key, + &pk.data_len); + buffer = silc_attribute_payload_encode(buffer, attribute, pk.data ? + SILC_ATTRIBUTE_FLAG_VALID : + SILC_ATTRIBUTE_FLAG_INVALID, + &pk, sizeof(pk)); + silc_free(pk.data); + if (!buffer) + return NULL; + break; + } + + /* No public key available */ + buffer = silc_attribute_payload_encode(buffer, attribute, + SILC_ATTRIBUTE_FLAG_INVALID, + NULL, 0); + if (!buffer) + return NULL; + break; - return attrs; + default: + /* Ignore SERVER_PUBLIC_KEY since we are going to put it anyway later */ + if (attribute == SILC_ATTRIBUTE_SERVER_PUBLIC_KEY || + attribute == SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE) + break; + + /* For other attributes we cannot reply so mark it invalid */ + buffer = silc_attribute_payload_encode(buffer, attribute, + SILC_ATTRIBUTE_FLAG_INVALID, + NULL, 0); + if (!buffer) + return NULL; + break; + } + } + + /* Always put our public key. This assures that we send at least + something valid back always. */ + pk.type = "silc-rsa"; + pk.data = silc_pkcs_public_key_encode(server->public_key, &pk.data_len); + buffer = silc_attribute_payload_encode(buffer, + SILC_ATTRIBUTE_SERVER_PUBLIC_KEY, + pk.data ? SILC_ATTRIBUTE_FLAG_VALID : + SILC_ATTRIBUTE_FLAG_INVALID, + &pk, sizeof(pk)); + silc_free(pk.data); + if (!buffer) + return NULL; + + /* Finally compute the digital signature of all the data we provided + as an indication that we provided rightfull information, and this + also authenticates our public key. */ + if (silc_pkcs_private_key_get_len(server->private_key) / 8 <= + sizeof(sign) -1 && + silc_pkcs_sign(server->private_key, buffer->data, + silc_buffer_len(buffer), sign, sizeof(sign), &sign_len, + TRUE, server->sha1hash)) { + pk.type = NULL; + pk.data = sign; + pk.data_len = sign_len; + buffer = + silc_attribute_payload_encode(buffer, + SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE, + SILC_ATTRIBUTE_FLAG_VALID, + &pk, sizeof(pk)); + } + if (!buffer) + return NULL; + + return buffer; } /* Find client by the Client ID indicated by the `client_id', and if not @@ -1533,8 +2140,8 @@ unsigned char *silc_server_query_reply_attrs(SilcServer server, SilcClientEntry silc_server_query_client(SilcServer server, const SilcClientID *client_id, - bool always_resolve, - bool *resolved) + SilcBool always_resolve, + SilcBool *resolved) { SilcClientEntry client; @@ -1561,6 +2168,9 @@ SilcClientEntry silc_server_query_client(SilcServer server, always_resolve) { SilcBuffer buffer, idp; + /* Statistics */ + server->stat.commands_sent++; + if (client) { client->data.status |= SILC_IDLIST_STATUS_RESOLVING; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED; @@ -1570,11 +2180,12 @@ SilcClientEntry silc_server_query_client(SilcServer server, idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS, server->cmd_ident, 1, - 4, idp->data, idp->len); + 4, idp->data, + silc_buffer_len(idp)); silc_server_packet_send(server, client ? client->router->connection : SILC_PRIMARY_ROUTE(server), SILC_PACKET_COMMAND, 0, - buffer->data, buffer->len, FALSE); + buffer->data, silc_buffer_len(buffer)); silc_buffer_free(idp); silc_buffer_free(buffer);