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);
418 query->ids[query->ids_count].id = id;
419 query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
424 /* Get the max count of reply messages allowed */
425 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
426 if (tmp && tmp_len == sizeof(SilcUInt32))
427 SILC_GET32_MSB(query->reply_count, tmp);
429 /* Get requested attributes if set */
430 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
432 query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
435 case SILC_COMMAND_WHOWAS:
437 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
439 silc_server_query_send_error(server, query,
440 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
441 silc_server_query_free(query);
445 /* Get the nickname@server string and parse it */
447 !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
448 silc_server_query_send_error(server, query,
449 SILC_STATUS_ERR_BAD_NICKNAME, 0);
450 silc_server_query_free(query);
454 /* Get the max count of reply messages allowed */
455 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
456 if (tmp && tmp_len == sizeof(SilcUInt32))
457 SILC_GET32_MSB(query->reply_count, tmp);
460 case SILC_COMMAND_IDENTIFY:
461 /* Get IDs if present. Take IDs always instead of names. */
462 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
465 /* Try get nickname */
466 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
468 /* Get the nickname@server string and parse it */
470 !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
471 silc_server_query_add_error(server, query, TRUE, 1,
472 SILC_STATUS_ERR_BAD_NICKNAME);
475 /* Try get server name */
476 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
478 query->server_name = silc_memdup(tmp, tmp_len);
480 /* Get channel name */
481 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
482 if (tmp && tmp_len <= 256)
483 query->channel_name = silc_memdup(tmp, tmp_len);
485 if (!query->nickname && !query->server_name && !query->channel_name) {
486 silc_server_query_send_error(server, query,
487 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
488 silc_server_query_free(query);
493 /* Parse the IDs included in the query */
494 query->ids = silc_calloc(argc, sizeof(*query->ids));
496 for (i = 0; i < argc; i++) {
497 tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
501 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
503 silc_server_query_add_error(server, query, TRUE, i + 5,
504 SILC_STATUS_ERR_BAD_CLIENT_ID);
508 /* Normal server must check whether this ID exist, and if not then
509 send the query to router, unless done so already */
510 if (server->server_type == SILC_SERVER && !query->resolved) {
511 if (!silc_idlist_find_client_by_id(server->local_list,
513 if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
514 !silc_idlist_find_client_by_id(server->global_list,
516 silc_server_query_send_router(server, query);
517 for (i = 0; i < query->ids_count; i++)
518 silc_free(query->ids[i].id);
519 silc_free(query->ids);
526 query->ids[query->ids_count].id = id;
527 query->ids[query->ids_count].id_type = id_type;
532 /* Get the max count of reply messages allowed */
533 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
534 if (tmp && tmp_len == sizeof(SilcUInt32))
535 SILC_GET32_MSB(query->reply_count, tmp);
539 /* Start processing the query information */
540 silc_server_query_process(server, query, TRUE);
543 /* Processes the parsed query. This does the actual finding of the
544 queried information and prepares for sending reply to the original
545 sender of the query command. */
547 void silc_server_query_process(SilcServer server, SilcServerQuery query,
550 SilcServerCommandContext cmd = query->cmd;
551 bool check_global = FALSE;
553 SilcClientEntry *clients = NULL, client_entry;
554 SilcChannelEntry *channels = NULL;
555 SilcServerEntry *servers = NULL;
556 SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
559 SILC_LOG_DEBUG(("Processing %s query",
560 silc_get_command_name(query->querycmd)));
562 /* Check global lists if query is coming from client or we are not
563 normal server (we know global information). */
564 if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
566 else if (server->server_type != SILC_SERVER)
569 if (query->nickname) {
570 /* Get all clients matching nickname from local list */
571 if (!silc_idlist_get_clients_by_hash(server->local_list,
572 query->nickname, server->md5hash,
573 &clients, &clients_count))
574 silc_idlist_get_clients_by_nickname(server->local_list,
577 &clients, &clients_count);
579 /* Check global list as well */
581 if (!silc_idlist_get_clients_by_hash(server->global_list,
582 query->nickname, server->md5hash,
583 &clients, &clients_count))
584 silc_idlist_get_clients_by_nickname(server->global_list,
587 &clients, &clients_count);
591 silc_server_query_add_error(server, query, TRUE, 1,
592 SILC_STATUS_ERR_NO_SUCH_NICK);
595 if (query->server_name) {
596 /* Find server by name */
597 entry = silc_idlist_find_server_by_name(server->local_list,
598 query->server_name, TRUE, NULL);
599 if (!entry && check_global)
600 entry = silc_idlist_find_server_by_name(server->global_list,
601 query->server_name, TRUE, NULL);
603 servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
604 servers[servers_count++] = (SilcServerEntry)entry;
608 silc_server_query_add_error(server, query, TRUE, 2,
609 SILC_STATUS_ERR_NO_SUCH_SERVER);
612 if (query->channel_name) {
613 /* Find channel by name */
614 entry = silc_idlist_find_channel_by_name(server->local_list,
615 query->channel_name, NULL);
616 if (!entry && check_global)
617 entry = silc_idlist_find_channel_by_name(server->global_list,
618 query->channel_name, NULL);
620 channels = silc_realloc(channels, sizeof(*channels) *
621 (channels_count + 1));
622 channels[channels_count++] = (SilcChannelEntry)entry;
626 silc_server_query_add_error(server, query, TRUE, 3,
627 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
630 if (query->ids_count) {
631 /* Find entries by the queried IDs */
632 for (i = 0; i < query->ids_count; i++) {
633 void *id = query->ids[i].id;
637 switch (query->ids[i].id_type) {
640 /* Get client entry */
641 entry = silc_idlist_find_client_by_id(server->local_list,
643 if (!entry && check_global)
644 entry = silc_idlist_find_client_by_id(server->global_list,
647 silc_server_query_add_error(server, query, FALSE, i,
648 SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
652 clients = silc_realloc(clients, sizeof(*clients) *
653 (clients_count + 1));
654 clients[clients_count++] = (SilcClientEntry)entry;
658 /* Get server entry */
659 entry = silc_idlist_find_server_by_id(server->local_list,
661 if (!entry && check_global)
662 entry = silc_idlist_find_server_by_id(server->global_list,
665 silc_server_query_add_error(server, query, FALSE, i,
666 SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
670 servers = silc_realloc(servers, sizeof(*servers) *
671 (servers_count + 1));
672 servers[servers_count++] = (SilcServerEntry)entry;
675 case SILC_ID_CHANNEL:
676 /* Get channel entry */
677 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
678 if (!entry && check_global)
679 entry = silc_idlist_find_channel_by_id(server->global_list, id,
682 silc_server_query_add_error(server, query, FALSE, i,
683 SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
687 channels = silc_realloc(channels, sizeof(*channels) *
688 (channels_count + 1));
689 channels[channels_count++] = (SilcChannelEntry)entry;
698 /* If nothing was found, then just send the errors */
699 if (!clients && !channels && !servers) {
700 silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
704 /* If caller does not want us to resolve anything (has resolved already)
705 then just continue with sending the reply */
707 silc_server_query_send_reply(server, query, clients, clients_count,
708 servers, servers_count, channels,
716 /* Now process all found information and if necessary do some more
718 switch (query->querycmd) {
720 case SILC_COMMAND_WHOIS:
721 for (i = 0; i < clients_count; i++) {
722 client_entry = clients[i];
724 /* Check if cannot query this anyway, so take next one */
726 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
729 /* If Requested Attributes is set then we always resolve the client
730 information, if not then check whether the entry is complete or not
731 and decide whether we need to resolve or not. */
734 /* Even if nickname and stuff are present, we may need to resolve
736 if (client_entry->nickname && client_entry->username &&
737 client_entry->userinfo) {
738 /* Check if cannot query this anyway, so take next one */
739 if (!client_entry->router)
742 /* If we are router, client is local to us, or client is on channel
743 we do not need to resolve the client information. */
744 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
745 || silc_hash_table_count(client_entry->channels) ||
751 /* When requested attributes is present and local client is detached
752 we cannot send the command to the client, we'll reply on behalf of
753 the client instead. */
754 if (query->attrs && SILC_IS_LOCAL(client_entry) &&
755 (client_entry->mode & SILC_UMODE_DETACHED ||
756 client_entry->data.status & SILC_IDLIST_STATUS_NOATTR))
759 /* Resolve the detailed client information. If client is local we
760 know that attributes were present and we will resolve directly
761 from the client. Otherwise resolve from client's owner. */
762 silc_server_query_resolve(server, query,
763 (SILC_IS_LOCAL(client_entry) ?
764 client_entry->connection :
765 client_entry->router->connection),
770 case SILC_COMMAND_WHOWAS:
771 for (i = 0; i < clients_count; i++) {
772 client_entry = clients[i];
774 /* Check if cannot query this anyway, so take next one */
775 if (!client_entry || !client_entry->router ||
776 client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
779 /* If both nickname and username are present no resolving is needed */
780 if (client_entry->nickname && client_entry->username)
783 /* Resolve the detailed client information */
784 silc_server_query_resolve(server, query,
785 client_entry->router->connection,
790 case SILC_COMMAND_IDENTIFY:
791 for (i = 0; i < clients_count; i++) {
792 client_entry = clients[i];
794 /* Check if cannot query this anyway, so take next one */
795 if (!client_entry || !client_entry->router ||
796 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
799 /* Even if nickname is present, we may need to resolve the entry */
800 if (client_entry->nickname) {
802 /* If we are router, client is local to us, or client is on channel
803 we do not need to resolve the client information. */
804 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
805 || silc_hash_table_count(client_entry->channels) ||
810 /* Resolve the detailed client information */
811 silc_server_query_resolve(server, query,
812 client_entry->router->connection,
818 if (!query->queries_count)
819 /* If we didn't have to do any resolving, continue with sending the
820 command reply to the original sender. */
821 silc_server_query_send_reply(server, query, clients, clients_count,
822 servers, servers_count, channels,
825 /* Now actually send the resolvings we gathered earlier */
826 silc_server_query_resolve(server, query, NULL, NULL);
833 /* Resolve the detailed information for the `client_entry'. Only client
834 information needs to be resolved for being incomplete. Each incomplete
835 client entry calls this function to do the resolving. */
837 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
838 SilcSocketConnection sock,
839 SilcClientEntry client_entry)
841 SilcServerCommandContext cmd = query->cmd;
842 SilcServerQueryList r = NULL;
849 if (!sock && client_entry)
852 /* If arguments are NULL we will now actually send the resolvings
853 that earlier has been gathered by calling this function. */
854 if (!sock && !client_entry) {
857 SILC_LOG_DEBUG(("Sending the resolvings"));
859 /* WHOWAS resolving has been done at the same time this function
860 was called to add the resolving for WHOWAS, so just return. */
861 if (query->querycmd == SILC_COMMAND_WHOWAS)
864 for (i = 0; i < query->querylist_count; i++) {
865 r = &query->querylist[i];
867 /* Send WHOIS command */
868 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
869 r->argc, r->arg, r->arg_lens,
870 r->arg_types, r->ident);
871 silc_server_packet_send(server, r->sock, SILC_PACKET_COMMAND, 0,
872 res_cmd->data, res_cmd->len, FALSE);
873 silc_buffer_free(res_cmd);
875 /* Reprocess this packet after received reply */
876 if (silc_server_command_pending_timed(server, SILC_COMMAND_WHOIS,
878 silc_server_query_resolve_reply,
880 query->queries_left++;
883 /* Cleanup this temporary context */
884 for (i = 0; i < query->querylist_count; i++) {
886 for (k = 0; k < query->querylist[i].argc; k++)
887 silc_free(query->querylist[i].arg[k]);
888 silc_free(query->querylist[i].arg);
889 silc_free(query->querylist[i].arg_lens);
890 silc_free(query->querylist[i].arg_types);
892 silc_free(query->querylist);
893 query->querylist = NULL;
894 query->querylist_count = 0;
898 SILC_LOG_DEBUG(("Resolving client information"));
900 if (client_entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
901 /* The entry is being resolved by some other external query already.
902 Attach to that query instead of resolving again. */
903 ident = client_entry->resolve_cmd_ident;
904 if (silc_server_command_pending(server, SILC_COMMAND_NONE, ident,
905 silc_server_query_resolve_reply, query))
906 query->queries_left++;
908 /* This entry will be resolved */
909 ident = ++server->cmd_ident;
911 switch (query->querycmd) {
913 case SILC_COMMAND_WHOIS:
914 case SILC_COMMAND_IDENTIFY:
915 /* Take existing query context if exist for this connection */
916 for (i = 0; i < query->queries_count; i++)
917 if (query->querylist[i].sock == sock) {
918 r = &query->querylist[i];
923 /* Allocate new temp query list context */
924 query->querylist = silc_realloc(query->querylist,
925 sizeof(*query->querylist) *
926 (query->querylist_count + 1));
927 r = &query->querylist[query->querylist_count];
928 query->querylist_count++;
929 memset(r, 0, sizeof(*r));
932 if (SILC_IS_LOCAL(client_entry))
936 /* If Requested Attributes were present put them to this resolving */
937 if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
939 r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
940 r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
941 r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
943 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
945 r->arg[r->argc] = silc_memdup(tmp, len);
946 r->arg_lens[r->argc] = len;
947 r->arg_types[r->argc] = 3;
952 r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
953 r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
954 r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
956 /* Add the client entry to be resolved */
957 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
958 r->arg[r->argc] = silc_memdup(idp->data, idp->len);
959 r->arg_lens[r->argc] = idp->len;
960 r->arg_types[r->argc] = r->argc + 4;
962 silc_buffer_free(idp);
966 case SILC_COMMAND_WHOWAS:
967 /* We must send WHOWAS command since it's the only the way of
968 resolving clients that are not present in the network anymore. */
969 silc_server_send_command(server, sock, query->querycmd, ident, 1,
970 1, query->nickname, strlen(query->nickname));
971 if (silc_server_command_pending(server, query->querycmd, ident,
972 silc_server_query_resolve_reply, query))
973 query->queries_left++;
978 /* Mark the entry as being resolved */
979 client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
980 client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
981 client_entry->resolve_cmd_ident = ident;
983 /* Save the queried ID, which we will reprocess after we get this and
984 all other queries back. */
985 query->queries = silc_realloc(query->queries, sizeof(*query->queries) *
986 (query->queries_count + 1));
987 if (query->queries) {
988 i = query->queries_count;
989 query->queries[i].id = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
990 query->queries[i].id_type = SILC_ID_CLIENT;
991 query->queries[i].ident = ident;
992 query->queries_count++;
996 /* Reply callback called after one resolving has been completed. If
997 all resolvings has been received then we will continue with sending
998 the command reply to the original sender of the query. */
1000 void silc_server_query_resolve_reply(void *context, void *reply)
1002 SilcServerQuery query = context;
1003 SilcServer server = query->cmd->server;
1004 SilcServerCommandReplyContext cmdr = reply;
1005 SilcUInt16 ident = cmdr->ident;
1006 SilcStatus error = SILC_STATUS_OK;
1007 SilcServerQueryID id = NULL;
1008 SilcClientEntry client_entry;
1011 /* One less query left */
1012 query->queries_left--;
1014 silc_command_get_status(cmdr->payload, NULL, &error);
1015 SILC_LOG_DEBUG(("Received reply to resolving (%d left) (status=%d)",
1016 query->queries_left, error));
1018 /* If no error then skip to other stuff */
1019 if (error == SILC_STATUS_OK)
1022 /* Error occurred during resolving */
1024 /* Find the resolved client ID */
1025 for (i = 0; i < query->queries_count; i++) {
1026 if (query->queries[i].ident != ident)
1029 id = &query->queries[i];
1031 if (error == SILC_STATUS_ERR_TIMEDOUT) {
1033 /* If timeout occurred for local entry when resolving attributes
1034 mark that this client doesn't support attributes in WHOIS. This
1035 assures we won't send the request again to the client. */
1036 if (query->querycmd == SILC_COMMAND_WHOIS && query->attrs) {
1037 client_entry = silc_idlist_find_client_by_id(server->local_list,
1038 id->id, TRUE, NULL);
1039 SILC_LOG_DEBUG(("Client %s does not support Requested Attributes",
1040 silc_id_render(id->id, SILC_ID_CLIENT)));
1041 if (client_entry && SILC_IS_LOCAL(client_entry)) {
1042 client_entry->data.status |= SILC_IDLIST_STATUS_NOATTR;
1043 client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
1048 /* Remove the RESOLVING status from the client entry */
1049 if (query->querycmd != SILC_COMMAND_WHOWAS) {
1050 client_entry = silc_idlist_find_client_by_id(server->local_list,
1051 id->id, TRUE, NULL);
1053 client_entry = silc_idlist_find_client_by_id(server->global_list,
1054 id->id, TRUE, NULL);
1056 client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
1063 /* If there are queries left then wait for them */
1064 if (query->queries_left)
1067 SILC_LOG_DEBUG(("Reprocess the query"));
1069 /* We have received all queries. Now re-search all information required
1070 to complete this query. Reason we cannot save the values found in
1071 the first search is that SilcClientEntry, SilcServerEntry and
1072 SilcChannelEntry pointers may become invalid while we were waiting
1073 for these resolvings. */
1074 silc_server_query_process(server, query, FALSE);
1077 /* Send the reply to the original query. If arguments are NULL then this
1078 sends only the errors that has occurred during the processing of the
1079 query. This sends the errors always after sending all the found
1080 information. The query is over after this function returns and the
1081 `query' will become invalid. This is called only after all informations
1082 has been resolved. This means that if something is not found or is
1083 incomplete in this function we were unable to resolve the information
1084 or it does not exist at all. */
1086 void silc_server_query_send_reply(SilcServer server,
1087 SilcServerQuery query,
1088 SilcClientEntry *clients,
1089 SilcUInt32 clients_count,
1090 SilcServerEntry *servers,
1091 SilcUInt32 servers_count,
1092 SilcChannelEntry *channels,
1093 SilcUInt32 channels_count)
1095 SilcServerCommandContext cmd = query->cmd;
1096 SilcUInt16 ident = silc_command_get_ident(cmd->payload);
1101 int i, k, valid_count;
1102 char nh[256], uh[256];
1103 bool sent_reply = FALSE;
1105 SILC_LOG_DEBUG(("Sending reply to query"));
1107 status = SILC_STATUS_OK;
1110 if (clients_count) {
1111 SilcClientEntry entry;
1112 SilcSocketConnection hsock;
1114 /* Mark all invalid entries */
1115 for (i = 0, valid_count = 0; i < clients_count; i++) {
1117 switch (query->querycmd) {
1118 case SILC_COMMAND_WHOIS:
1119 if (!entry->nickname || !entry->username || !entry->userinfo ||
1120 !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1121 /* When querying by ID, every "unfound" entry must cause error */
1123 silc_server_query_add_error_id(server, query,
1124 SILC_STATUS_ERR_TIMEDOUT,
1125 entry->id, SILC_ID_CLIENT);
1131 case SILC_COMMAND_IDENTIFY:
1132 if (!entry->nickname ||
1133 !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1134 /* When querying by ID, every "unfound" entry must cause error */
1136 silc_server_query_add_error_id(server, query,
1137 SILC_STATUS_ERR_TIMEDOUT,
1138 entry->id, SILC_ID_CLIENT);
1144 case SILC_COMMAND_WHOWAS:
1145 if (!entry->nickname || !entry->username ||
1146 entry->data.status & SILC_IDLIST_STATUS_REGISTERED) {
1155 /* Start processing found clients */
1156 status = SILC_STATUS_OK;
1157 if (valid_count > 1)
1158 status = SILC_STATUS_LIST_START;
1160 /* Now do the sending of valid entries */
1162 for (i = 0; i < clients_count && valid_count; i++) {
1168 status = SILC_STATUS_LIST_ITEM;
1169 if (valid_count > 1 && k == valid_count - 1
1170 && !servers_count && !channels_count && !query->errors_count)
1171 status = SILC_STATUS_LIST_END;
1172 if (query->reply_count && k - 1 == query->reply_count)
1173 status = SILC_STATUS_LIST_END;
1175 SILC_LOG_DEBUG(("%s: client %s",
1176 (status == SILC_STATUS_OK ? " OK" :
1177 status == SILC_STATUS_LIST_START ? "START" :
1178 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1179 status == SILC_STATUS_LIST_END ? " END" :
1180 " : "), entry->nickname));
1182 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
1183 memset(uh, 0, sizeof(uh));
1184 memset(nh, 0, sizeof(nh));
1186 silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname));
1187 if (!strchr(entry->nickname, '@')) {
1188 silc_strncat(nh, sizeof(nh), "@", 1);
1189 if (entry->servername) {
1190 silc_strncat(nh, sizeof(nh), entry->servername,
1191 strlen(entry->servername));
1193 len = entry->router ? strlen(entry->router->server_name) :
1194 strlen(server->server_name);
1195 silc_strncat(nh, sizeof(nh), entry->router ?
1196 entry->router->server_name :
1197 server->server_name, len);
1201 switch (query->querycmd) {
1203 case SILC_COMMAND_WHOIS:
1205 unsigned char idle[4], mode[4];
1206 unsigned char *fingerprint, fempty[20], *attrs = NULL;
1207 SilcBuffer channels, umode_list = NULL;
1209 memset(fempty, 0, sizeof(fempty));
1210 silc_strncat(uh, sizeof(uh), entry->username,
1211 strlen(entry->username));
1212 if (!strchr(entry->username, '@') && entry->connection) {
1213 hsock = entry->connection;
1214 silc_strncat(uh, sizeof(uh), "@", 1);
1215 len = strlen(hsock->hostname);
1216 silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1219 if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
1221 silc_server_get_client_channel_list(server, entry, FALSE,
1222 FALSE, &umode_list);
1225 silc_server_get_client_channel_list(server, entry, TRUE,
1228 if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
1229 fingerprint = entry->data.fingerprint;
1233 SILC_PUT32_MSB(entry->mode, mode);
1234 if (entry->connection)
1235 SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
1237 /* If Requested Attribute were present, and we do not have the
1238 attributes we will reply to them on behalf of the client. */
1241 if (!entry->attrs) {
1242 attrs = silc_server_query_reply_attrs(server, query, &len);
1244 attrs = entry->attrs;
1245 len = entry->attrs_len;
1249 /* Send command reply */
1250 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1251 status, 0, ident, 10,
1252 2, idp->data, idp->len,
1256 strlen(entry->userinfo),
1257 6, channels ? channels->data : NULL,
1258 channels ? channels->len : 0,
1262 fingerprint ? 20 : 0,
1263 10, umode_list ? umode_list->data :
1264 NULL, umode_list ? umode_list->len :
1269 /* For now we will delete Requested Attributes */
1270 silc_free(entry->attrs);
1271 entry->attrs = NULL;
1274 silc_buffer_free(channels);
1276 silc_buffer_free(umode_list);
1282 case SILC_COMMAND_IDENTIFY:
1283 if (!entry->username) {
1284 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1285 status, 0, ident, 2,
1286 2, idp->data, idp->len,
1290 silc_strncat(uh, sizeof(uh), entry->username,
1291 strlen(entry->username));
1292 if (!strchr(entry->username, '@') && entry->connection) {
1293 hsock = entry->connection;
1294 silc_strncat(uh, sizeof(uh), "@", 1);
1295 len = strlen(hsock->hostname);
1296 silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1299 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1300 status, 0, ident, 3,
1301 2, idp->data, idp->len,
1308 case SILC_COMMAND_WHOWAS:
1309 silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
1310 if (!strchr(entry->username, '@'))
1311 silc_strncat(uh, sizeof(uh), "@*private*", 10);
1313 /* Send command reply */
1314 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1315 status, 0, ident, 4,
1316 2, idp->data, idp->len,
1321 strlen(entry->userinfo) : 0);
1326 silc_buffer_free(idp);
1328 if (status == SILC_STATUS_LIST_END)
1334 /* Not one valid entry was found, send error. If nickname was used
1335 in query send error based on that, otherwise the query->errors
1336 already includes proper errors. */
1337 if (query->nickname)
1338 silc_server_query_add_error(server, query, TRUE, 1,
1339 SILC_STATUS_ERR_NO_SUCH_NICK);
1344 if (query->querycmd == SILC_COMMAND_IDENTIFY && servers_count) {
1345 SilcServerEntry entry;
1347 if (status == SILC_STATUS_OK && servers_count > 1)
1348 status = SILC_STATUS_LIST_START;
1351 for (i = 0; i < servers_count; i++) {
1355 status = SILC_STATUS_LIST_ITEM;
1356 if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
1357 !query->errors_count)
1358 status = SILC_STATUS_LIST_END;
1359 if (query->reply_count && k - 1 == query->reply_count)
1360 status = SILC_STATUS_LIST_END;
1362 SILC_LOG_DEBUG(("%s: server %s",
1363 (status == SILC_STATUS_OK ? " OK" :
1364 status == SILC_STATUS_LIST_START ? "START" :
1365 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1366 status == SILC_STATUS_LIST_END ? " END" :
1368 entry->server_name ? entry->server_name : ""));
1370 /* Send command reply */
1371 idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
1372 silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1373 status, 0, ident, 2,
1374 2, idp->data, idp->len,
1375 3, entry->server_name,
1376 entry->server_name ?
1377 strlen(entry->server_name) : 0);
1378 silc_buffer_free(idp);
1381 if (status == SILC_STATUS_LIST_END)
1388 if (query->querycmd == SILC_COMMAND_IDENTIFY && channels_count) {
1389 SilcChannelEntry entry;
1391 if (status == SILC_STATUS_OK && channels_count > 1)
1392 status = SILC_STATUS_LIST_START;
1395 for (i = 0; i < channels_count; i++) {
1396 entry = channels[i];
1399 status = SILC_STATUS_LIST_ITEM;
1400 if (channels_count > 1 && k == channels_count - 1 &&
1401 !query->errors_count)
1402 status = SILC_STATUS_LIST_END;
1403 if (query->reply_count && k - 1 == query->reply_count)
1404 status = SILC_STATUS_LIST_END;
1406 SILC_LOG_DEBUG(("%s: channel %s",
1407 (status == SILC_STATUS_OK ? " OK" :
1408 status == SILC_STATUS_LIST_START ? "START" :
1409 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1410 status == SILC_STATUS_LIST_END ? " END" :
1412 entry->channel_name ? entry->channel_name : ""));
1414 /* Send command reply */
1415 idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
1416 silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1417 status, 0, ident, 2,
1418 2, idp->data, idp->len,
1419 3, entry->channel_name,
1420 entry->channel_name ?
1421 strlen(entry->channel_name) : 0);
1422 silc_buffer_free(idp);
1425 if (status == SILC_STATUS_LIST_END)
1432 if (query->errors_count) {
1435 if (status == SILC_STATUS_OK && query->errors_count > 1)
1436 status = SILC_STATUS_LIST_START;
1439 for (i = 0; i < query->errors_count; i++) {
1442 /* Take error argument */
1443 if (query->errors[i].from_cmd) {
1445 tmp = silc_argument_get_arg_type(cmd->args,
1446 query->errors[i].index, &len);
1447 if (query->errors[i].index == 1)
1448 type = 3; /* Nickname */
1451 } else if (!query->errors[i].id) {
1453 silc_id_payload_encode(query->ids[query->errors[i].index].id,
1454 query->ids[query->errors[k].index].id_type);
1459 idp = silc_id_payload_encode(query->errors[i].id,
1460 query->errors[k].id_type);
1467 status = SILC_STATUS_LIST_ITEM;
1468 if (query->errors_count > 1 && k == query->errors_count - 1)
1469 status = SILC_STATUS_LIST_END;
1470 if (query->reply_count && k - 1 == query->reply_count)
1471 status = SILC_STATUS_LIST_END;
1473 SILC_LOG_DEBUG(("%s: ERROR: %s (%d)",
1474 (status == SILC_STATUS_OK ? " OK" :
1475 status == SILC_STATUS_LIST_START ? "START" :
1476 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1477 status == SILC_STATUS_LIST_END ? " END" :
1479 silc_get_status_message(query->errors[i].error),
1480 query->errors[i].error));
1483 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1484 (status == SILC_STATUS_OK ?
1485 query->errors[i].error : status),
1486 (status == SILC_STATUS_OK ?
1487 0 : query->errors[i].error), ident, 1,
1489 silc_buffer_free(idp);
1492 if (status == SILC_STATUS_LIST_END)
1499 SILC_LOG_ERROR(("BUG: Query did not send anything"));
1502 silc_server_query_free(query);
1505 /* This routine is used to reply to Requested Attributes in WHOIS on behalf
1506 of the client since we were unable to resolve them from the client.
1507 Either client does not support Requested Attributes or isn't replying
1508 to them like it should. */
1510 unsigned char *silc_server_query_reply_attrs(SilcServer server,
1511 SilcServerQuery query,
1512 SilcUInt32 *attrs_len)
1514 unsigned char *attrs = NULL;
1517 SILC_LOG_DEBUG(("Constructing Requested Attributes"));
1525 /* Find client by the Client ID indicated by the `client_id', and if not
1526 found then query it by using WHOIS command. The client information
1527 is also resolved if the cached information is incomplete or if the
1528 `always_resolve' is set to TRUE. The indication whether requested
1529 client was being resolved is saved into `resolved'. If the client
1530 is not being resolved its entry is returned by this function. NULL
1531 is returned if client is resolved. */
1533 SilcClientEntry silc_server_query_client(SilcServer server,
1534 const SilcClientID *client_id,
1535 bool always_resolve,
1538 SilcClientEntry client;
1540 SILC_LOG_DEBUG(("Resolving client by client ID"));
1545 client = silc_idlist_find_client_by_id(server->local_list,
1546 (SilcClientID *)client_id,
1549 client = silc_idlist_find_client_by_id(server->global_list,
1550 (SilcClientID *)client_id,
1552 if (!client && server->server_type == SILC_ROUTER)
1556 if (!client && server->standalone)
1559 if (!client || !client->nickname || !client->username ||
1561 SilcBuffer buffer, idp;
1564 client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
1565 client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
1566 client->resolve_cmd_ident = ++server->cmd_ident;
1569 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
1570 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
1571 server->cmd_ident, 1,
1572 4, idp->data, idp->len);
1573 silc_server_packet_send(server, client ? client->router->connection :
1574 SILC_PRIMARY_ROUTE(server),
1575 SILC_PACKET_COMMAND, 0,
1576 buffer->data, buffer->len, FALSE);
1577 silc_buffer_free(idp);
1578 silc_buffer_free(buffer);