5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 /* XXX TODO Requested Attributes to WHOIS */
23 #include "serverincludes.h"
24 #include "server_internal.h"
27 SilcSocketConnection sock; /* Connection of this query */
28 unsigned char **arg; /* Query argument */
29 SilcUInt32 *arg_lens; /* Query argument lengths */
30 SilcUInt32 *arg_types; /* Query argument types */
31 SilcUInt32 argc; /* Number of query arguments */
32 SilcUInt32 timeout; /* Max timeout for query to complete */
33 SilcUInt16 ident; /* Query command identifier */
34 } *SilcServerQueryList;
36 /* Represents an SILC ID */
39 SilcIdType id_type; /* ID type */
40 SilcUInt16 ident; /* Command identifier */
43 /* Represents one error occurred during query */
46 SilcIdType id_type; /* ID type */
47 SilcUInt16 index; /* Index to IDs */
48 unsigned int from_cmd : 1; /* TRUE if `index' is from command args,
49 otherwise from query->ids */
50 unsigned int error : 7; /* The actual error (SilcStatus) */
51 } *SilcServerQueryError;
53 /* Query session context */
56 char *nickname; /* Queried nickname */
57 char *nick_server; /* Queried nickname's server */
58 char *server_name; /* Queried server name */
59 char *channel_name; /* Queried channel name */
60 SilcServerQueryID ids; /* Queried IDs */
61 SilcUInt32 ids_count; /* number of queried IDs */
62 SilcUInt32 reply_count; /* Requested reply count */
63 SilcDList attrs; /* Requested Attributes in WHOIS */
65 /* Query session data */
66 SilcServerCommandContext cmd; /* Command context for query */
67 SilcServerQueryList querylist; /* Temporary query list context */
68 SilcServerQueryID queries; /* Ongoing queries */
69 SilcServerQueryError errors; /* Query errors */
70 SilcUInt16 querylist_count; /* Number of query lists */
71 SilcUInt16 queries_count; /* Number of ongoing queries */
72 SilcUInt16 queries_left; /* Number of ongoing queries left */
73 SilcUInt16 errors_count; /* number of errors */
74 unsigned int querycmd : 7; /* Query command (SilcCommand) */
75 unsigned int resolved : 1; /* TRUE if normal server has resolved
76 information from router */
79 void silc_server_query_free(SilcServerQuery query);
80 void silc_server_query_send_error(SilcServer server,
81 SilcServerQuery query,
82 SilcStatus error, ...);
83 void silc_server_query_add_error(SilcServer server,
84 SilcServerQuery query,
88 void silc_server_query_add_error_id(SilcServer server,
89 SilcServerQuery query,
91 void *id, SilcIdType id_type);
92 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
93 void silc_server_query_send_router_reply(void *context, void *reply);
94 void silc_server_query_parse(SilcServer server, SilcServerQuery query);
95 void silc_server_query_process(SilcServer server, SilcServerQuery query,
97 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
98 SilcSocketConnection sock,
99 SilcClientEntry client_entry);
100 void silc_server_query_resolve_reply(void *context, void *reply);
101 void silc_server_query_send_reply(SilcServer server,
102 SilcServerQuery query,
103 SilcClientEntry *clients,
104 SilcUInt32 clients_count,
105 SilcServerEntry *servers,
106 SilcUInt32 servers_count,
107 SilcChannelEntry *channels,
108 SilcUInt32 channels_count);
109 unsigned char *silc_server_query_reply_attrs(SilcServer server,
110 SilcServerQuery query,
111 SilcUInt32 *attrs_len);
113 /* Free the query context structure and all allocated resources. */
115 void silc_server_query_free(SilcServerQuery query)
119 silc_server_command_free(query->cmd);
121 for (i = 0; i < query->queries_count; i++)
122 silc_free(query->queries[i].id);
123 silc_free(query->queries);
125 silc_free(query->nickname);
126 silc_free(query->nick_server);
127 silc_free(query->server_name);
128 silc_free(query->channel_name);
130 for (i = 0; i < query->ids_count; i++)
131 silc_free(query->ids[i].id);
132 silc_free(query->ids);
135 silc_attribute_payload_list_free(query->attrs);
137 for (i = 0; i < query->errors_count; i++)
138 silc_free(query->errors[i].id);
139 silc_free(query->errors);
141 memset(query, 'F', sizeof(*query));
145 /* Send error reply indicated by the `error' to the original sender of
148 void silc_server_query_send_error(SilcServer server,
149 SilcServerQuery query,
150 SilcStatus error, ...)
153 unsigned char *data = NULL;
154 SilcUInt32 data_len = 0, data_type = 0, argc = 0;
157 data_type = va_arg(va, SilcUInt32);
160 data = va_arg(va, unsigned char *);
161 data_len = va_arg(va, SilcUInt32);
164 SILC_LOG_DEBUG(("ERROR: %s (%d)", silc_get_status_message(error), error));
166 /* Send the command reply with error */
167 silc_server_send_command_reply(server, query->cmd->sock,
168 query->querycmd, error, 0,
169 silc_command_get_ident(query->cmd->payload),
170 argc, data_type, data, data_len);
174 /* Add error to error list. Multiple errors may occur during the query
175 processing and this function can be used to add one error. The
176 `index' is the index to the command context which includes the argument
177 which caused the error, or it is the index to query->ids, depending
178 on value of `from_cmd'. */
180 void silc_server_query_add_error(SilcServer server,
181 SilcServerQuery query,
186 query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
187 (query->errors_count + 1));
190 query->errors[query->errors_count].index = index;
191 query->errors[query->errors_count].from_cmd = from_cmd;
192 query->errors[query->errors_count].error = error;
193 query->errors[query->errors_count].id = NULL;
194 query->errors[query->errors_count].id_type = 0;
195 query->errors_count++;
198 /* Same as silc_server_query_add_error but adds the ID data to be used
199 with error sending with this error type. */
201 void silc_server_query_add_error_id(SilcServer server,
202 SilcServerQuery query,
204 void *id, SilcIdType id_type)
206 query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
207 (query->errors_count + 1));
210 query->errors[query->errors_count].index = 0;
211 query->errors[query->errors_count].from_cmd = FALSE;
212 query->errors[query->errors_count].error = error;
213 query->errors[query->errors_count].id = silc_id_dup(id, id_type);
214 query->errors[query->errors_count].id_type = id_type;
215 query->errors_count++;
218 /* Processes query as command. The `query' is the command that is
219 being processed indicated by the `cmd'. The `query' can be one of
220 the following: SILC_COMMAND_WHOIS, SILC_COMMAND_WHOWAS or
221 SILC_COMMAND_IDENTIFY. This function handles the reply sending
222 to the entity who sent this query to us automatically. Returns
223 TRUE if the query is being processed or FALSE on error. */
225 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
226 SilcServerCommandContext cmd)
228 SilcServerQuery query;
230 SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd)));
232 query = silc_calloc(1, sizeof(*query));
233 query->querycmd = querycmd;
234 query->cmd = silc_server_command_dup(cmd);
238 case SILC_COMMAND_WHOIS:
239 /* If we are normal server and query contains nickname, send it
240 directly to router. */
241 if (server->server_type == SILC_SERVER && !server->standalone &&
242 cmd->sock != SILC_PRIMARY_ROUTE(server) &&
243 silc_argument_get_arg_type(cmd->args, 1, NULL)) {
244 silc_server_query_send_router(server, query);
249 case SILC_COMMAND_WHOWAS:
250 /* WHOWAS query is always sent to router if we are normal server */
251 if (server->server_type == SILC_SERVER && !server->standalone &&
252 cmd->sock != SILC_PRIMARY_ROUTE(server)) {
253 silc_server_query_send_router(server, query);
258 case SILC_COMMAND_IDENTIFY:
259 /* If we are normal server and query does not contain IDs, send it
260 directly to router (it contains nickname, server name or channel
262 if (server->server_type == SILC_SERVER && !server->standalone &&
263 cmd->sock != SILC_PRIMARY_ROUTE(server) &&
264 !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
265 silc_server_query_send_router(server, query);
271 SILC_LOG_ERROR(("Bad query using %d command", querycmd));
272 silc_server_query_free(query);
276 /* Now parse the request */
277 silc_server_query_parse(server, query);
282 /* Send the received query to our primary router since we could not
283 handle the query directly. We will reprocess the query after our
284 router replies back. */
286 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
289 SilcUInt16 old_ident;
291 SILC_LOG_DEBUG(("Forwarding the query to router for processing"));
293 /* Send WHOIS command to our router */
294 old_ident = silc_command_get_ident(query->cmd->payload);
295 silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
296 tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
297 silc_server_packet_send(server,
298 SILC_PRIMARY_ROUTE(server),
299 SILC_PACKET_COMMAND, 0,
300 tmpbuf->data, tmpbuf->len, TRUE);
301 silc_command_set_ident(query->cmd->payload, old_ident);
302 silc_buffer_free(tmpbuf);
304 query->resolved = TRUE;
306 /* Continue parsing the query after received reply from router */
307 silc_server_command_pending(server, query->querycmd, server->cmd_ident,
308 silc_server_query_send_router_reply, query);
311 /* Reply callback called after primary router has replied to our initial
312 sending of the query to it. We will proceed the query in this function. */
314 void silc_server_query_send_router_reply(void *context, void *reply)
316 SilcServerQuery query = context;
317 SilcServer server = query->cmd->server;
318 SilcServerCommandReplyContext cmdr = reply;
320 SILC_LOG_DEBUG(("Received reply from router to query"));
322 /* Check if router sent error reply */
323 if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
326 SILC_LOG_DEBUG(("Sending error to original query"));
328 /* Send the same command reply payload which contains the error */
329 silc_command_set_command(cmdr->payload, query->querycmd);
330 silc_command_set_ident(cmdr->payload,
331 silc_command_get_ident(query->cmd->payload));
332 buffer = silc_command_payload_encode_payload(cmdr->payload);
333 silc_server_packet_send(server, query->cmd->sock,
334 SILC_PACKET_COMMAND_REPLY, 0,
335 buffer->data, buffer->len, FALSE);
336 silc_buffer_free(buffer);
337 silc_server_query_free(query);
341 /* Continue with parsing */
342 silc_server_query_parse(server, query);
345 /* Parse the command query and start processing the queries in detail. */
347 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
349 SilcServerCommandContext cmd = query->cmd;
351 SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
356 SILC_LOG_DEBUG(("Parsing %s query",
357 silc_get_command_name(query->querycmd)));
359 switch (query->querycmd) {
361 case SILC_COMMAND_WHOIS:
362 /* Get Client IDs if present. Take IDs always instead of nickname. */
363 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
367 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
369 silc_server_query_send_error(server, query,
370 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
371 silc_server_query_free(query);
375 /* Get the nickname@server string and parse it */
377 !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
378 silc_server_query_send_error(server, query,
379 SILC_STATUS_ERR_BAD_NICKNAME, 0);
380 silc_server_query_free(query);
385 /* Parse the IDs included in the query */
386 query->ids = silc_calloc(argc, sizeof(*query->ids));
388 for (i = 0; i < argc; i++) {
389 tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
393 id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
395 silc_server_query_add_error(server, query, TRUE, i + 4,
396 SILC_STATUS_ERR_BAD_CLIENT_ID);
400 /* Normal server must check whether this ID exist, and if not then
401 send the query to router, unless done so already */
402 if (server->server_type == SILC_SERVER && !query->resolved) {
403 if (!silc_idlist_find_client_by_id(server->local_list,
405 if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
406 !silc_idlist_find_client_by_id(server->global_list,
408 silc_server_query_send_router(server, query);
409 for (i = 0; i < query->ids_count; i++)
410 silc_free(query->ids[i].id);
411 silc_free(query->ids);
417 query->ids[query->ids_count].id = id;
418 query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
423 /* Get the max count of reply messages allowed */
424 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
425 if (tmp && tmp_len == sizeof(SilcUInt32))
426 SILC_GET32_MSB(query->reply_count, tmp);
428 /* Get requested attributes if set */
429 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
431 query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
434 case SILC_COMMAND_WHOWAS:
436 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
438 silc_server_query_send_error(server, query,
439 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
440 silc_server_query_free(query);
444 /* Get the nickname@server string and parse it */
446 !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
447 silc_server_query_send_error(server, query,
448 SILC_STATUS_ERR_BAD_NICKNAME, 0);
449 silc_server_query_free(query);
453 /* Get the max count of reply messages allowed */
454 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
455 if (tmp && tmp_len == sizeof(SilcUInt32))
456 SILC_GET32_MSB(query->reply_count, tmp);
459 case SILC_COMMAND_IDENTIFY:
460 /* Get IDs if present. Take IDs always instead of names. */
461 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
464 /* Try get nickname */
465 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
467 /* Get the nickname@server string and parse it */
469 !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
470 silc_server_query_add_error(server, query, TRUE, 1,
471 SILC_STATUS_ERR_BAD_NICKNAME);
474 /* Try get server name */
475 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
477 query->server_name = silc_memdup(tmp, tmp_len);
479 /* Get channel name */
480 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
481 if (tmp && tmp_len <= 256)
482 query->channel_name = silc_memdup(tmp, tmp_len);
485 /* Parse the IDs included in the query */
486 query->ids = silc_calloc(argc, sizeof(*query->ids));
488 for (i = 0; i < argc; i++) {
489 tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
493 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
495 silc_server_query_add_error(server, query, TRUE, i + 5,
496 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
500 /* Normal server must check whether this ID exist, and if not then
501 send the query to router, unless done so already */
502 if (server->server_type == SILC_SERVER && !query->resolved) {
503 if (!silc_idlist_find_client_by_id(server->local_list,
505 if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
506 !silc_idlist_find_client_by_id(server->global_list,
508 silc_server_query_send_router(server, query);
509 for (i = 0; i < query->ids_count; i++)
510 silc_free(query->ids[i].id);
511 silc_free(query->ids);
517 query->ids[query->ids_count].id = id;
518 query->ids[query->ids_count].id_type = id_type;
523 /* Get the max count of reply messages allowed */
524 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
525 if (tmp && tmp_len == sizeof(SilcUInt32))
526 SILC_GET32_MSB(query->reply_count, tmp);
530 /* Start processing the query information */
531 silc_server_query_process(server, query, TRUE);
534 /* Processes the parsed query. This does the actual finding of the
535 queried information and prepares for sending reply to the original
536 sender of the query command. */
538 void silc_server_query_process(SilcServer server, SilcServerQuery query,
541 SilcServerCommandContext cmd = query->cmd;
542 bool check_global = FALSE;
544 SilcClientEntry *clients = NULL, client_entry;
545 SilcChannelEntry *channels = NULL;
546 SilcServerEntry *servers = NULL;
547 SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
550 SILC_LOG_DEBUG(("Processing %s query",
551 silc_get_command_name(query->querycmd)));
553 /* Check global lists if query is coming from client or we are not
554 normal server (we know global information). */
555 if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
557 else if (server->server_type != SILC_SERVER)
560 if (query->nickname) {
561 /* Get all clients matching nickname from local list */
562 if (!silc_idlist_get_clients_by_hash(server->local_list,
563 query->nickname, server->md5hash,
564 &clients, &clients_count))
565 silc_idlist_get_clients_by_nickname(server->local_list,
568 &clients, &clients_count);
570 /* Check global list as well */
572 if (!silc_idlist_get_clients_by_hash(server->global_list,
573 query->nickname, server->md5hash,
574 &clients, &clients_count))
575 silc_idlist_get_clients_by_nickname(server->global_list,
578 &clients, &clients_count);
582 silc_server_query_add_error(server, query, TRUE, 1,
583 SILC_STATUS_ERR_NO_SUCH_NICK);
586 if (query->server_name) {
587 /* Find server by name */
588 entry = silc_idlist_find_server_by_name(server->local_list,
589 query->server_name, TRUE, NULL);
590 if (!entry && check_global)
591 entry = silc_idlist_find_server_by_name(server->global_list,
592 query->server_name, TRUE, NULL);
594 servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
595 servers[servers_count++] = (SilcServerEntry)entry;
599 silc_server_query_add_error(server, query, TRUE, 2,
600 SILC_STATUS_ERR_NO_SUCH_SERVER);
603 if (query->channel_name) {
604 /* Find channel by name */
605 entry = silc_idlist_find_channel_by_name(server->local_list,
606 query->channel_name, NULL);
607 if (!entry && check_global)
608 entry = silc_idlist_find_channel_by_name(server->global_list,
609 query->channel_name, NULL);
611 channels = silc_realloc(channels, sizeof(*channels) *
612 (channels_count + 1));
613 channels[channels_count++] = (SilcChannelEntry)entry;
617 silc_server_query_add_error(server, query, TRUE, 3,
618 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
621 if (query->ids_count) {
622 /* Find entries by the queried IDs */
623 for (i = 0; i < query->ids_count; i++) {
624 void *id = query->ids[i].id;
628 switch (query->ids[i].id_type) {
631 /* Get client entry */
632 entry = silc_idlist_find_client_by_id(server->local_list,
634 if (!entry && check_global)
635 entry = silc_idlist_find_client_by_id(server->global_list,
638 silc_server_query_add_error(server, query, FALSE, i,
639 SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
643 clients = silc_realloc(clients, sizeof(*clients) *
644 (clients_count + 1));
645 clients[clients_count++] = (SilcClientEntry)entry;
649 /* Get server entry */
650 entry = silc_idlist_find_server_by_id(server->local_list,
652 if (!entry && check_global)
653 entry = silc_idlist_find_server_by_id(server->global_list,
656 silc_server_query_add_error(server, query, FALSE, i,
657 SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
661 servers = silc_realloc(servers, sizeof(*servers) *
662 (servers_count + 1));
663 servers[servers_count++] = (SilcServerEntry)entry;
666 case SILC_ID_CHANNEL:
667 /* Get channel entry */
668 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
669 if (!entry && check_global)
670 entry = silc_idlist_find_channel_by_id(server->global_list, id,
673 silc_server_query_add_error(server, query, FALSE, i,
674 SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
678 channels = silc_realloc(channels, sizeof(*channels) *
679 (channels_count + 1));
680 channels[channels_count++] = (SilcChannelEntry)entry;
689 /* If nothing was found, then just send the errors */
690 if (!clients && !channels && !servers) {
691 silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
695 /* If caller does not want us to resolve anything (has resolved already)
696 then just continue with sending the reply */
698 silc_server_query_send_reply(server, query, clients, clients_count,
699 servers, servers_count, channels,
707 /* Now process all found information and if necessary do some more
709 switch (query->querycmd) {
711 case SILC_COMMAND_WHOIS:
712 for (i = 0; i < clients_count; i++) {
713 client_entry = clients[i];
715 /* Check if cannot query this anyway, so take next one */
717 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
720 /* If Requested Attributes is set then we always resolve the client
721 information, if not then check whether the entry is complete or not
722 and decide whether we need to resolve or not. */
725 /* Even if nickname and stuff are present, we may need to resolve
727 if (client_entry->nickname && client_entry->username &&
728 client_entry->userinfo) {
729 /* Check if cannot query this anyway, so take next one */
730 if (!client_entry->router)
733 /* If we are router, client is local to us, or client is on channel
734 we do not need to resolve the client information. */
735 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
736 || silc_hash_table_count(client_entry->channels) ||
742 /* When requested attributes is present and local client is detached
743 we cannot send the command to the client, we'll reply on behalf of
744 the client instead. */
745 if (query->attrs && SILC_IS_LOCAL(client_entry) &&
746 (client_entry->mode & SILC_UMODE_DETACHED ||
747 client_entry->data.status & SILC_IDLIST_STATUS_NOATTR))
750 /* Resolve the detailed client information. If client is local we
751 know that attributes were present and we will resolve directly
752 from the client. Otherwise resolve from client's owner. */
753 silc_server_query_resolve(server, query,
754 (SILC_IS_LOCAL(client_entry) ?
755 client_entry->connection :
756 client_entry->router->connection),
761 case SILC_COMMAND_WHOWAS:
762 for (i = 0; i < clients_count; i++) {
763 client_entry = clients[i];
765 /* Check if cannot query this anyway, so take next one */
766 if (!client_entry || !client_entry->router ||
767 client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
770 /* If both nickname and username are present no resolving is needed */
771 if (client_entry->nickname && client_entry->username)
774 /* Resolve the detailed client information */
775 silc_server_query_resolve(server, query,
776 client_entry->router->connection,
781 case SILC_COMMAND_IDENTIFY:
782 for (i = 0; i < clients_count; i++) {
783 client_entry = clients[i];
785 /* Check if cannot query this anyway, so take next one */
786 if (!client_entry || !client_entry->router ||
787 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
790 /* Even if nickname is present, we may need to resolve the entry */
791 if (client_entry->nickname) {
793 /* If we are router, client is local to us, or client is on channel
794 we do not need to resolve the client information. */
795 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
796 || silc_hash_table_count(client_entry->channels) ||
801 /* Resolve the detailed client information */
802 silc_server_query_resolve(server, query,
803 client_entry->router->connection,
809 if (!query->queries_count)
810 /* If we didn't have to do any resolving, continue with sending the
811 command reply to the original sender. */
812 silc_server_query_send_reply(server, query, clients, clients_count,
813 servers, servers_count, channels,
816 /* Now actually send the resolvings we gathered earlier */
817 silc_server_query_resolve(server, query, NULL, NULL);
824 /* Resolve the detailed information for the `client_entry'. Only client
825 information needs to be resolved for being incomplete. Each incomplete
826 client entry calls this function to do the resolving. */
828 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
829 SilcSocketConnection sock,
830 SilcClientEntry client_entry)
832 SilcServerCommandContext cmd = query->cmd;
833 SilcServerQueryList r = NULL;
840 if (!sock && client_entry)
843 /* If arguments are NULL we will now actually send the resolvings
844 that earlier has been gathered by calling this function. */
845 if (!sock && !client_entry) {
848 SILC_LOG_DEBUG(("Sending the resolvings"));
850 /* WHOWAS resolving has been done at the same time this function
851 was called to add the resolving for WHOWAS, so just return. */
852 if (query->querycmd == SILC_COMMAND_WHOWAS)
855 for (i = 0; i < query->querylist_count; i++) {
856 r = &query->querylist[i];
858 /* Send WHOIS command */
859 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
860 r->argc, r->arg, r->arg_lens,
861 r->arg_types, r->ident);
862 silc_server_packet_send(server, r->sock, SILC_PACKET_COMMAND, 0,
863 res_cmd->data, res_cmd->len, FALSE);
864 silc_buffer_free(res_cmd);
866 /* Reprocess this packet after received reply */
867 if (silc_server_command_pending_timed(server, SILC_COMMAND_WHOIS,
869 silc_server_query_resolve_reply,
871 query->queries_left++;
874 /* Cleanup this temporary context */
875 for (i = 0; i < query->querylist_count; i++) {
877 for (k = 0; k < query->querylist[i].argc; k++)
878 silc_free(query->querylist[i].arg[k]);
879 silc_free(query->querylist[i].arg);
880 silc_free(query->querylist[i].arg_lens);
881 silc_free(query->querylist[i].arg_types);
883 silc_free(query->querylist);
884 query->querylist = NULL;
885 query->querylist_count = 0;
889 SILC_LOG_DEBUG(("Resolving client information"));
891 if (client_entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
892 /* The entry is being resolved by some other external query already.
893 Attach to that query instead of resolving again. */
894 ident = client_entry->resolve_cmd_ident;
895 if (silc_server_command_pending(server, SILC_COMMAND_NONE, ident,
896 silc_server_query_resolve_reply, query))
897 query->queries_left++;
899 /* This entry will be resolved */
900 ident = ++server->cmd_ident;
902 switch (query->querycmd) {
904 case SILC_COMMAND_WHOIS:
905 case SILC_COMMAND_IDENTIFY:
906 /* Take existing query context if exist for this connection */
907 for (i = 0; i < query->queries_count; i++)
908 if (query->querylist[i].sock == sock) {
909 r = &query->querylist[i];
914 /* Allocate new temp query list context */
915 query->querylist = silc_realloc(query->querylist,
916 sizeof(*query->querylist) *
917 (query->querylist_count + 1));
918 r = &query->querylist[query->querylist_count];
919 query->querylist_count++;
920 memset(r, 0, sizeof(*r));
923 if (SILC_IS_LOCAL(client_entry))
927 /* If Requested Attributes were present put them to this resolving */
928 if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
930 r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
931 r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
932 r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
934 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
936 r->arg[r->argc] = silc_memdup(tmp, len);
937 r->arg_lens[r->argc] = len;
938 r->arg_types[r->argc] = 3;
943 r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
944 r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
945 r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
947 /* Add the client entry to be resolved */
948 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
949 r->arg[r->argc] = silc_memdup(idp->data, idp->len);
950 r->arg_lens[r->argc] = idp->len;
951 r->arg_types[r->argc] = r->argc + 4;
953 silc_buffer_free(idp);
957 case SILC_COMMAND_WHOWAS:
958 /* We must send WHOWAS command since it's the only the way of
959 resolving clients that are not present in the network anymore. */
960 silc_server_send_command(server, sock, query->querycmd, ident, 1,
961 1, query->nickname, strlen(query->nickname));
962 if (silc_server_command_pending(server, query->querycmd, ident,
963 silc_server_query_resolve_reply, query))
964 query->queries_left++;
969 /* Mark the entry as being resolved */
970 client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
971 client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
972 client_entry->resolve_cmd_ident = ident;
974 /* Save the queried ID, which we will reprocess after we get this and
975 all other queries back. */
976 query->queries = silc_realloc(query->queries, sizeof(*query->queries) *
977 (query->queries_count + 1));
978 if (query->queries) {
979 i = query->queries_count;
980 query->queries[i].id = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
981 query->queries[i].id_type = SILC_ID_CLIENT;
982 query->queries[i].ident = ident;
983 query->queries_count++;
987 /* Reply callback called after one resolving has been completed. If
988 all resolvings has been received then we will continue with sending
989 the command reply to the original sender of the query. */
991 void silc_server_query_resolve_reply(void *context, void *reply)
993 SilcServerQuery query = context;
994 SilcServer server = query->cmd->server;
995 SilcServerCommandReplyContext cmdr = reply;
996 SilcUInt16 ident = cmdr->ident;
997 SilcStatus error = SILC_STATUS_OK;
998 SilcServerQueryID id = NULL;
999 SilcClientEntry client_entry;
1002 /* One less query left */
1003 query->queries_left--;
1005 silc_command_get_status(cmdr->payload, NULL, &error);
1006 SILC_LOG_DEBUG(("Received reply to resolving (%d left) (status=%d)",
1007 query->queries_left, error));
1009 /* If no error then skip to other stuff */
1010 if (error == SILC_STATUS_OK)
1013 /* Error occurred during resolving */
1015 /* Find the resolved client ID */
1016 for (i = 0; i < query->queries_count; i++) {
1017 if (query->queries[i].ident != ident)
1020 id = &query->queries[i];
1022 if (error == SILC_STATUS_ERR_TIMEDOUT) {
1023 /* If timeout occurred for local entry when resolving attributes
1024 mark that this client doesn't support attributes in WHOIS. This
1025 assures we won't send the request again to the client. */
1026 if (query->querycmd == SILC_COMMAND_WHOIS && query->attrs) {
1027 client_entry = silc_idlist_find_client_by_id(server->local_list,
1028 id->id, TRUE, NULL);
1029 SILC_LOG_DEBUG(("Client %s does not support Requested Attributes",
1030 silc_id_render(id->id, SILC_ID_CLIENT)));
1031 if (client_entry && SILC_IS_LOCAL(client_entry))
1032 client_entry->data.status |= SILC_IDLIST_STATUS_NOATTR;
1039 /* If there are queries left then wait for them */
1040 if (query->queries_left)
1043 SILC_LOG_DEBUG(("Reprocess the query"));
1045 /* We have received all queries. Now re-search all information required
1046 to complete this query. Reason we cannot save the values found in
1047 the first search is that SilcClientEntry, SilcServerEntry and
1048 SilcChannelEntry pointers may become invalid while we were waiting
1049 for these resolvings. */
1050 silc_server_query_process(server, query, FALSE);
1053 /* Send the reply to the original query. If arguments are NULL then this
1054 sends only the errors that has occurred during the processing of the
1055 query. This sends the errors always after sending all the found
1056 information. The query is over after this function returns and the
1057 `query' will become invalid. This is called only after all informations
1058 has been resolved. This means that if something is not found or is
1059 incomplete in this function we were unable to resolve the information
1060 or it does not exist at all. */
1062 void silc_server_query_send_reply(SilcServer server,
1063 SilcServerQuery query,
1064 SilcClientEntry *clients,
1065 SilcUInt32 clients_count,
1066 SilcServerEntry *servers,
1067 SilcUInt32 servers_count,
1068 SilcChannelEntry *channels,
1069 SilcUInt32 channels_count)
1071 SilcServerCommandContext cmd = query->cmd;
1072 SilcUInt16 ident = silc_command_get_ident(cmd->payload);
1077 int i, k, valid_count;
1078 char nh[256], uh[256];
1079 bool sent_reply = FALSE;
1081 SILC_LOG_DEBUG(("Sending reply to query"));
1083 status = SILC_STATUS_OK;
1086 if (clients_count) {
1087 SilcClientEntry entry;
1088 SilcSocketConnection hsock;
1090 /* Mark all invalid entries */
1091 for (i = 0, valid_count = 0; i < clients_count; i++) {
1093 switch (query->querycmd) {
1094 case SILC_COMMAND_WHOIS:
1095 if (!entry->nickname || !entry->username || !entry->userinfo ||
1096 !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1097 /* When querying by ID, every "unfound" entry must cause error */
1099 silc_server_query_add_error_id(server, query,
1100 SILC_STATUS_ERR_TIMEDOUT,
1101 entry->id, SILC_ID_CLIENT);
1107 case SILC_COMMAND_IDENTIFY:
1108 if (!entry->nickname ||
1109 !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1110 /* When querying by ID, every "unfound" entry must cause error */
1112 silc_server_query_add_error_id(server, query,
1113 SILC_STATUS_ERR_TIMEDOUT,
1114 entry->id, SILC_ID_CLIENT);
1120 case SILC_COMMAND_WHOWAS:
1121 if (!entry->nickname || !entry->username ||
1122 entry->data.status & SILC_IDLIST_STATUS_REGISTERED) {
1131 /* Start processing found clients */
1132 status = SILC_STATUS_OK;
1133 if (valid_count > 1)
1134 status = SILC_STATUS_LIST_START;
1136 /* Now do the sending of valid entries */
1138 for (i = 0; i < clients_count && valid_count; i++) {
1144 status = SILC_STATUS_LIST_ITEM;
1145 if (valid_count > 1 && k == valid_count - 1
1146 && !servers_count && !channels_count && !query->errors_count)
1147 status = SILC_STATUS_LIST_END;
1148 if (query->reply_count && k - 1 == query->reply_count)
1149 status = SILC_STATUS_LIST_END;
1151 SILC_LOG_DEBUG(("%s: client %s",
1152 (status == SILC_STATUS_OK ? " OK" :
1153 status == SILC_STATUS_LIST_START ? "START" :
1154 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1155 status == SILC_STATUS_LIST_END ? " END" :
1156 " : "), entry->nickname));
1158 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
1159 memset(uh, 0, sizeof(uh));
1160 memset(nh, 0, sizeof(nh));
1162 silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname));
1163 if (!strchr(entry->nickname, '@')) {
1164 silc_strncat(nh, sizeof(nh), "@", 1);
1165 if (entry->servername) {
1166 silc_strncat(nh, sizeof(nh), entry->servername,
1167 strlen(entry->servername));
1169 len = entry->router ? strlen(entry->router->server_name) :
1170 strlen(server->server_name);
1171 silc_strncat(nh, sizeof(nh), entry->router ?
1172 entry->router->server_name :
1173 server->server_name, len);
1177 switch (query->querycmd) {
1179 case SILC_COMMAND_WHOIS:
1181 unsigned char idle[4], mode[4];
1182 unsigned char *fingerprint, fempty[20], *attrs;
1183 SilcBuffer channels, umode_list = NULL;
1185 memset(fempty, 0, sizeof(fempty));
1186 silc_strncat(uh, sizeof(uh), entry->username,
1187 strlen(entry->username));
1188 if (!strchr(entry->username, '@') && entry->connection) {
1189 hsock = entry->connection;
1190 silc_strncat(uh, sizeof(uh), "@", 1);
1191 len = strlen(hsock->hostname);
1192 silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1195 if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
1197 silc_server_get_client_channel_list(server, entry, FALSE,
1198 FALSE, &umode_list);
1201 silc_server_get_client_channel_list(server, entry, TRUE,
1204 if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
1205 fingerprint = entry->data.fingerprint;
1209 SILC_PUT32_MSB(entry->mode, mode);
1210 if (entry->connection)
1211 SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
1213 /* If Requested Attribute were present, and we do not have the
1214 attributes we will reply to them on behalf of the client. */
1216 if (!entry->attrs) {
1217 attrs = silc_server_query_reply_attrs(server, query, &len);
1219 attrs = entry->attrs;
1220 len = entry->attrs_len;
1224 /* Send command reply */
1225 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1226 status, 0, ident, 9,
1227 2, idp->data, idp->len,
1231 strlen(entry->userinfo),
1232 6, channels ? channels->data : NULL,
1233 channels ? channels->len : 0,
1237 fingerprint ? 20 : 0,
1238 10, umode_list ? umode_list->data :
1239 NULL, umode_list ? umode_list->len :
1243 /* For now we will delete Requested Attributes */
1244 silc_free(entry->attrs);
1245 entry->attrs = NULL;
1248 silc_buffer_free(channels);
1250 silc_buffer_free(umode_list);
1256 case SILC_COMMAND_IDENTIFY:
1257 if (!entry->username) {
1258 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1259 status, 0, ident, 2,
1260 2, idp->data, idp->len,
1264 silc_strncat(uh, sizeof(uh), entry->username,
1265 strlen(entry->username));
1266 if (!strchr(entry->username, '@') && entry->connection) {
1267 hsock = entry->connection;
1268 silc_strncat(uh, sizeof(uh), "@", 1);
1269 len = strlen(hsock->hostname);
1270 silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1273 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1274 status, 0, ident, 3,
1275 2, idp->data, idp->len,
1282 case SILC_COMMAND_WHOWAS:
1283 silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
1284 if (!strchr(entry->username, '@'))
1285 silc_strncat(uh, sizeof(uh), "@*private*", 10);
1287 /* Send command reply */
1288 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1289 status, 0, ident, 4,
1290 2, idp->data, idp->len,
1295 strlen(entry->userinfo) : 0);
1300 silc_buffer_free(idp);
1302 if (status == SILC_STATUS_LIST_END)
1308 /* Not one valid entry was found, send error. If nickname was used
1309 in query send error based on that, otherwise the query->errors
1310 already includes proper errors. */
1311 if (query->nickname)
1312 silc_server_query_add_error(server, query, TRUE, 1,
1313 SILC_STATUS_ERR_NO_SUCH_NICK);
1318 if (query->querycmd == SILC_COMMAND_IDENTIFY && servers_count) {
1319 SilcServerEntry entry;
1321 if (status == SILC_STATUS_OK && servers_count > 1)
1322 status = SILC_STATUS_LIST_START;
1325 for (i = 0; i < servers_count; i++) {
1329 status = SILC_STATUS_LIST_ITEM;
1330 if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
1331 !query->errors_count)
1332 status = SILC_STATUS_LIST_END;
1333 if (query->reply_count && k - 1 == query->reply_count)
1334 status = SILC_STATUS_LIST_END;
1336 SILC_LOG_DEBUG(("%s: server %s",
1337 (status == SILC_STATUS_OK ? " OK" :
1338 status == SILC_STATUS_LIST_START ? "START" :
1339 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1340 status == SILC_STATUS_LIST_END ? " END" :
1342 entry->server_name ? entry->server_name : ""));
1344 /* Send command reply */
1345 idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
1346 silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1347 status, 0, ident, 2,
1348 2, idp->data, idp->len,
1349 3, entry->server_name,
1350 entry->server_name ?
1351 strlen(entry->server_name) : 0);
1352 silc_buffer_free(idp);
1355 if (status == SILC_STATUS_LIST_END)
1362 if (query->querycmd == SILC_COMMAND_IDENTIFY && channels_count) {
1363 SilcChannelEntry entry;
1365 if (status == SILC_STATUS_OK && channels_count > 1)
1366 status = SILC_STATUS_LIST_START;
1369 for (i = 0; i < channels_count; i++) {
1370 entry = channels[i];
1373 status = SILC_STATUS_LIST_ITEM;
1374 if (channels_count > 1 && k == channels_count - 1 &&
1375 !query->errors_count)
1376 status = SILC_STATUS_LIST_END;
1377 if (query->reply_count && k - 1 == query->reply_count)
1378 status = SILC_STATUS_LIST_END;
1380 SILC_LOG_DEBUG(("%s: channel %s",
1381 (status == SILC_STATUS_OK ? " OK" :
1382 status == SILC_STATUS_LIST_START ? "START" :
1383 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1384 status == SILC_STATUS_LIST_END ? " END" :
1386 entry->channel_name ? entry->channel_name : ""));
1388 /* Send command reply */
1389 idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
1390 silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1391 status, 0, ident, 2,
1392 2, idp->data, idp->len,
1393 3, entry->channel_name,
1394 entry->channel_name ?
1395 strlen(entry->channel_name) : 0);
1396 silc_buffer_free(idp);
1399 if (status == SILC_STATUS_LIST_END)
1406 if (query->errors_count) {
1409 if (status == SILC_STATUS_OK && query->errors_count > 1)
1410 status = SILC_STATUS_LIST_START;
1413 for (i = 0; i < query->errors_count; i++) {
1416 /* Take error argument */
1417 if (query->errors[i].from_cmd) {
1418 tmp = silc_argument_get_arg_type(cmd->args,
1419 query->errors[i].index, &len);
1420 if (query->errors[i].index == 1)
1421 type = 3; /* Nickname */
1424 } else if (!query->errors[i].id) {
1426 silc_id_payload_encode(query->ids[query->errors[i].index].id,
1427 query->ids[query->errors[k].index].id_type);
1432 idp = silc_id_payload_encode(query->errors[i].id,
1433 query->errors[k].id_type);
1440 status = SILC_STATUS_LIST_ITEM;
1441 if (query->errors_count > 1 && k == query->errors_count - 1)
1442 status = SILC_STATUS_LIST_END;
1443 if (query->reply_count && k - 1 == query->reply_count)
1444 status = SILC_STATUS_LIST_END;
1446 SILC_LOG_DEBUG(("%s: ERROR: %s (%d)",
1447 (status == SILC_STATUS_OK ? " OK" :
1448 status == SILC_STATUS_LIST_START ? "START" :
1449 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1450 status == SILC_STATUS_LIST_END ? " END" :
1452 silc_get_status_message(query->errors[i].error),
1453 query->errors[i].error));
1456 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1457 (status == SILC_STATUS_OK ?
1458 query->errors[i].error : status),
1459 (status == SILC_STATUS_OK ?
1460 0 : query->errors[i].error), ident, 1,
1462 silc_buffer_free(idp);
1465 if (status == SILC_STATUS_LIST_END)
1472 SILC_LOG_ERROR(("BUG: Query did not send anything"));
1475 silc_server_query_free(query);
1478 /* This routine is used to reply to Requested Attributes in WHOIS on behalf
1479 of the client since we were unable to resolve them from the client.
1480 Either client does not support Requested Attributes or isn't replying
1481 to them like it should. */
1483 unsigned char *silc_server_query_reply_attrs(SilcServer server,
1484 SilcServerQuery query,
1485 SilcUInt32 *attrs_len)
1487 unsigned char *attrs = NULL;
1490 SILC_LOG_DEBUG(("Constructing Requested Attributes"));
1498 /* Find client by the Client ID indicated by the `client_id', and if not
1499 found then query it by using WHOIS command. The client information
1500 is also resolved if the cached information is incomplete or if the
1501 `always_resolve' is set to TRUE. The indication whether requested
1502 client was being resolved is saved into `resolved'. If the client
1503 is not being resolved its entry is returned by this function. NULL
1504 is returned if client is resolved. */
1506 SilcClientEntry silc_server_query_client(SilcServer server,
1507 const SilcClientID *client_id,
1508 bool always_resolve,
1511 SilcClientEntry client;
1513 SILC_LOG_DEBUG(("Resolving client by client ID"));
1518 client = silc_idlist_find_client_by_id(server->local_list,
1519 (SilcClientID *)client_id,
1522 client = silc_idlist_find_client_by_id(server->global_list,
1523 (SilcClientID *)client_id,
1525 if (!client && server->server_type == SILC_ROUTER)
1529 if (!client && server->standalone)
1532 if (!client || !client->nickname || !client->username ||
1534 SilcBuffer buffer, idp;
1537 client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
1538 client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
1539 client->resolve_cmd_ident = ++server->cmd_ident;
1542 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
1543 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
1544 server->cmd_ident, 1,
1545 4, idp->data, idp->len);
1546 silc_server_packet_send(server, client ? client->router->connection :
1547 SILC_PRIMARY_ROUTE(server),
1548 SILC_PACKET_COMMAND, 0,
1549 buffer->data, buffer->len, FALSE);
1550 silc_buffer_free(idp);
1551 silc_buffer_free(buffer);