The SILC Project

source navigation ]
identifier search ]
freetext search ]
file search ]

silc/silcd/server_query.c

  1 /*
  2 
  3   server_query.c
  4 
  5   Author: Pekka Riikonen <priikone@silcnet.org>
  6 
  7   Copyright (C) 2002 - 2005 Pekka Riikonen
  8 
  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.
 12 
 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.
 17 
 18 */
 19 /* $Id: server_query.c,v 1.24 2005/04/23 13:32:24 priikone Exp $ */
 20 
 21 #include "serverincludes.h"
 22 #include "server_internal.h"
 23 
 24 typedef struct {
 25   SilcSocketConnection sock;        /* Connection of this query */
 26   unsigned char **arg;              /* Query argument */
 27   SilcUInt32 *arg_lens;             /* Query argument lengths */
 28   SilcUInt32 *arg_types;            /* Query argument types */
 29   SilcUInt32 argc;                  /* Number of query arguments */
 30   SilcUInt32 timeout;               /* Max timeout for query to complete */
 31   SilcUInt16 ident;                 /* Query command identifier */
 32 } *SilcServerQueryList;
 33 
 34 /* Represents an SILC ID */
 35 typedef struct {
 36   void *id;                         /* ID */
 37   SilcIdType id_type;               /* ID type */
 38   SilcUInt16 ident;                 /* Command identifier */
 39 } *SilcServerQueryID;
 40 
 41 /* Represents one error occurred during query */
 42 typedef struct {
 43   void *id;                         /* ID */
 44   SilcIdType id_type;               /* ID type */
 45   unsigned int index : 15;          /* Index to IDs */
 46   unsigned int type : 2;            /* 0 = take from query->ids, 1 = take
 47                                        from args, 2 = no args in error. */
 48   unsigned int error : 7;           /* The actual error (SilcStatus) */
 49 } *SilcServerQueryError;
 50 
 51 /* Query session context */
 52 typedef struct {
 53   /* Queried data */
 54   char *nickname;                   /* Queried nickname, normalized */
 55   char *nick_server;                /* Queried nickname's server */
 56   char *server_name;                /* Queried server name, normalized */
 57   char *channel_name;               /* Queried channel name, normalized */
 58   SilcServerQueryID ids;            /* Queried IDs */
 59   SilcUInt32 ids_count;             /* number of queried IDs */
 60   SilcUInt32 reply_count;           /* Requested reply count */
 61   SilcDList attrs;                  /* Requested Attributes in WHOIS */
 62 
 63   /* Query session data */
 64   SilcServerCommandContext cmd;     /* Command context for query */
 65   SilcServerQueryList querylist;    /* Temporary query list context */
 66   SilcServerQueryID queries;        /* Ongoing queries */
 67   SilcServerQueryError errors;      /* Query errors */
 68   SilcUInt16 querylist_count;       /* Number of query lists */
 69   SilcUInt16 queries_count;         /* Number of ongoing queries */
 70   SilcUInt16 queries_left;          /* Number of ongoing queries left */
 71   SilcUInt16 errors_count;          /* number of errors */
 72   unsigned int querycmd : 7;        /* Query command (SilcCommand) */
 73   unsigned int resolved : 1;        /* TRUE if normal server has resolved
 74                                        information from router */
 75 } *SilcServerQuery;
 76 
 77 void silc_server_query_free(SilcServerQuery query);
 78 void silc_server_query_send_error(SilcServer server,
 79                                   SilcServerQuery query,
 80                                   SilcStatus error, ...);
 81 void silc_server_query_add_error(SilcServer server,
 82                                  SilcServerQuery query,
 83                                  SilcUInt32 type,
 84                                  SilcUInt32 index,
 85                                  SilcStatus error);
 86 void silc_server_query_add_error_id(SilcServer server,
 87                                     SilcServerQuery query,
 88                                     SilcStatus error,
 89                                     void *id, SilcIdType id_type);
 90 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
 91 void silc_server_query_send_router_reply(void *context, void *reply);
 92 void silc_server_query_parse(SilcServer server, SilcServerQuery query);
 93 void silc_server_query_process(SilcServer server, SilcServerQuery query,
 94                                bool resolve);
 95 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
 96                                SilcSocketConnection sock,
 97                                SilcClientEntry client_entry);
 98 void silc_server_query_resolve_reply(void *context, void *reply);
 99 void silc_server_query_send_reply(SilcServer server,
100                                   SilcServerQuery query,
101                                   SilcClientEntry *clients,
102                                   SilcUInt32 clients_count,
103                                   SilcServerEntry *servers,
104                                   SilcUInt32 servers_count,
105                                   SilcChannelEntry *channels,
106                                   SilcUInt32 channels_count);
107 SilcBuffer silc_server_query_reply_attrs(SilcServer server,
108                                          SilcServerQuery query,
109                                          SilcClientEntry client_entry);
110 
111 /* Free the query context structure and all allocated resources. */
112 
113 void silc_server_query_free(SilcServerQuery query)
114 {
115   int i;
116 
117   silc_server_command_free(query->cmd);
118 
119   for (i = 0; i < query->queries_count; i++)
120     silc_free(query->queries[i].id);
121   silc_free(query->queries);
122 
123   silc_free(query->nickname);
124   silc_free(query->nick_server);
125   silc_free(query->server_name);
126   silc_free(query->channel_name);
127 
128   for (i = 0; i < query->ids_count; i++)
129     silc_free(query->ids[i].id);
130   silc_free(query->ids);
131 
132   if (query->attrs)
133     silc_attribute_payload_list_free(query->attrs);
134 
135   for (i = 0; i < query->errors_count; i++)
136     silc_free(query->errors[i].id);
137   silc_free(query->errors);
138 
139   memset(query, 'F', sizeof(*query));
140   silc_free(query);
141 }
142 
143 /* Send error reply indicated by the `error' to the original sender of
144    the query. */
145 
146 void silc_server_query_send_error(SilcServer server,
147                                   SilcServerQuery query,
148                                   SilcStatus error, ...)
149 {
150   va_list va;
151   unsigned char *data = NULL;
152   SilcUInt32 data_len = 0, data_type = 0, argc = 0;
153 
154   va_start(va, error);
155   data_type = va_arg(va, SilcUInt32);
156   if (data_type) {
157     argc = 1;
158     data = va_arg(va, unsigned char *);
159     data_len = va_arg(va, SilcUInt32);
160   }
161 
162   SILC_LOG_DEBUG(("ERROR: %s (%d)", silc_get_status_message(error), error));
163 
164   /* Send the command reply with error */
165   silc_server_send_command_reply(server, query->cmd->sock,
166                                  query->querycmd, error, 0,
167                                  silc_command_get_ident(query->cmd->payload),
168                                  argc, data_type, data, data_len);
169   va_end(va);
170 }
171 
172 /* Add error to error list.  Multiple errors may occur during the query
173    processing and this function can be used to add one error.  The
174    `index' is the index to the command context which includes the argument
175    which caused the error, or it is the index to query->ids, depending
176    on value of `type'.  If `type' is 0 the index is to query->ids, if
177    it is 1 it is index to the command context arguments, and if it is
178    2 the index is ignored and no argument is included in the error. */
179 
180 void silc_server_query_add_error(SilcServer server,
181                                  SilcServerQuery query,
182                                  SilcUInt32 type,
183                                  SilcUInt32 index,
184                                  SilcStatus error)
185 {
186   query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
187                                (query->errors_count + 1));
188   if (!query->errors)
189     return;
190   query->errors[query->errors_count].index = index;
191   query->errors[query->errors_count].type = type;
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++;
196 }
197 
198 /* Same as silc_server_query_add_error but adds the ID data to be used
199    with error sending with this error type. */
200 
201 void silc_server_query_add_error_id(SilcServer server,
202                                     SilcServerQuery query,
203                                     SilcStatus error,
204                                     void *id, SilcIdType id_type)
205 {
206   query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
207                                (query->errors_count + 1));
208   if (!query->errors)
209     return;
210   query->errors[query->errors_count].index = 0;
211   query->errors[query->errors_count].type = 0;
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++;
216 }
217 
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. */
224 
225 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
226                                SilcServerCommandContext cmd)
227 {
228   SilcServerQuery query;
229 
230   SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd)));
231 
232   query = silc_calloc(1, sizeof(*query));
233   query->querycmd = querycmd;
234   query->cmd = silc_server_command_dup(cmd);
235 
236   switch (querycmd) {
237 
238   case SILC_COMMAND_WHOIS:
239     /* If we are normal server and query contains nickname OR query
240        doesn't contain nickname or ids BUT attributes, send it to the
241        router */
242     if (server->server_type != SILC_ROUTER && !server->standalone &&
243         cmd->sock != SILC_PRIMARY_ROUTE(server) &&
244          (silc_argument_get_arg_type(cmd->args, 1, NULL) ||
245          (!silc_argument_get_arg_type(cmd->args, 1, NULL) &&
246           !silc_argument_get_arg_type(cmd->args, 4, NULL) &&
247           silc_argument_get_arg_type(cmd->args, 3, NULL)))) {
248       silc_server_query_send_router(server, query);
249       return TRUE;
250     }
251     break;
252 
253   case SILC_COMMAND_WHOWAS:
254     /* WHOWAS query is always sent to router if we are normal server */
255     if (server->server_type == SILC_SERVER && !server->standalone &&
256         cmd->sock != SILC_PRIMARY_ROUTE(server)) {
257       silc_server_query_send_router(server, query);
258       return TRUE;
259     }
260     break;
261 
262   case SILC_COMMAND_IDENTIFY:
263     /* If we are normal server and query does not contain IDs, send it
264        directly to router (it contains nickname, server name or channel
265        name). */
266     if (server->server_type == SILC_SERVER && !server->standalone &&
267         cmd->sock != SILC_PRIMARY_ROUTE(server) &&
268         !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
269       silc_server_query_send_router(server, query);
270       return TRUE;
271     }
272     break;
273 
274   default:
275     SILC_LOG_ERROR(("Bad query using %d command", querycmd));
276     silc_server_query_free(query);
277     return FALSE;
278   }
279 
280   /* Now parse the request */
281   silc_server_query_parse(server, query);
282 
283   return TRUE;
284 }
285 
286 /* Send the received query to our primary router since we could not
287    handle the query directly.  We will reprocess the query after our
288    router replies back. */
289 
290 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
291 {
292   SilcBuffer tmpbuf;
293   SilcUInt16 old_ident;
294 
295   SILC_LOG_DEBUG(("Forwarding the query to router for processing"));
296 
297   /* Statistics */
298   server->stat.commands_sent++;
299 
300   /* Send WHOIS command to our router */
301   old_ident = silc_command_get_ident(query->cmd->payload);
302   silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
303   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
304   silc_server_packet_send(server,
305                           SILC_PRIMARY_ROUTE(server),
306                           SILC_PACKET_COMMAND, 0,
307                           tmpbuf->data, tmpbuf->len, TRUE);
308   silc_command_set_ident(query->cmd->payload, old_ident);
309   silc_buffer_free(tmpbuf);
310 
311   query->resolved = TRUE;
312 
313   /* Continue parsing the query after received reply from router */
314   silc_server_command_pending(server, query->querycmd, server->cmd_ident,
315                               silc_server_query_send_router_reply, query);
316 }
317 
318 /* Reply callback called after primary router has replied to our initial
319    sending of the query to it.  We will proceed the query in this function. */
320 
321 void silc_server_query_send_router_reply(void *context, void *reply)
322 {
323   SilcServerQuery query = context;
324   SilcServer server = query->cmd->server;
325   SilcServerCommandReplyContext cmdr = reply;
326 
327   SILC_LOG_DEBUG(("Received reply from router to query"));
328 
329   /* If the original command caller has gone away, just stop. */
330   if (query->cmd->sock->users == 1) {
331     SILC_LOG_DEBUG(("Original command caller vanished"));
332     silc_server_query_free(query);
333     return;
334   }
335 
336   /* Check if router sent error reply */
337   if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
338     SilcBuffer buffer;
339 
340     SILC_LOG_DEBUG(("Sending error to original query"));
341 
342     /* Statistics */
343     server->stat.commands_sent++;
344 
345     /* Send the same command reply payload which contains the error */
346     silc_command_set_command(cmdr->payload, query->querycmd);
347     silc_command_set_ident(cmdr->payload,
348                            silc_command_get_ident(query->cmd->payload));
349     buffer = silc_command_payload_encode_payload(cmdr->payload);
350     silc_server_packet_send(server, query->cmd->sock,
351                             SILC_PACKET_COMMAND_REPLY, 0,
352                             buffer->data, buffer->len, FALSE);
353     silc_buffer_free(buffer);
354     silc_server_query_free(query);
355     return;
356   }
357 
358   /* Continue with parsing */
359   silc_server_query_parse(server, query);
360 }
361 
362 /* Parse the command query and start processing the queries in detail. */
363 
364 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
365 {
366   SilcServerCommandContext cmd = query->cmd;
367   unsigned char *tmp;
368   SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
369   void *id;
370   SilcIdType id_type;
371   int i;
372 
373   SILC_LOG_DEBUG(("Parsing %s query",
374                   silc_get_command_name(query->querycmd)));
375 
376   switch (query->querycmd) {
377 
378   case SILC_COMMAND_WHOIS:
379     /* Get requested attributes if set */
380     tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
381     if (tmp && !query->attrs && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN) {
382       query->attrs = silc_attribute_payload_parse(tmp, tmp_len);
383 
384       /* When Requested Attributes is present we will assure that this
385          client cannot execute the WHOIS command too fast.  This would be
386          same as having SILC_CF_LAG_STRICT. */
387       if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
388           cmd->sock->user_data)
389         ((SilcClientEntry)cmd->sock->user_data)->fast_command = 6;
390     }
391 
392     /* Get Client IDs if present. Take IDs always instead of nickname. */
393     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
394     if (!tmp) {
395 
396       /* Get nickname */
397       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
398       if (!tmp && !query->attrs) {
399         /* No nickname, no ids and no attributes - send error */
400         silc_server_query_send_error(server, query,
401                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
402         silc_server_query_free(query);
403         return;
404       }
405 
406       /* Get the nickname@server string and parse it */
407       if (tmp && ((tmp_len > 128) ||
408           !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))) {
409         silc_server_query_send_error(server, query,
410                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
411         silc_server_query_free(query);
412         return;
413       }
414 
415       /* Check nickname */
416       if (tmp) {
417         tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
418                                     SILC_STRING_UTF8, 128, &tmp_len);
419         if (!tmp) {
420           silc_server_query_send_error(server, query,
421                                        SILC_STATUS_ERR_BAD_NICKNAME, 0);
422           silc_server_query_free(query);
423           return;
424         }
425         silc_free(query->nickname);
426         query->nickname = tmp;
427       }
428 
429     } else {
430       /* Parse the IDs included in the query */
431       query->ids = silc_calloc(argc, sizeof(*query->ids));
432 
433       for (i = 0; i < argc; i++) {
434         tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
435         if (!tmp)
436           continue;
437 
438         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
439         if (!id || id_type != SILC_ID_CLIENT) {
440           silc_server_query_add_error(server, query, 1, i + 4,
441                                       SILC_STATUS_ERR_BAD_CLIENT_ID);
442           continue;
443         }
444 
445         /* Normal server must check whether this ID exist, and if not then
446            send the query to router, unless done so already */
447         if (server->server_type == SILC_SERVER && !query->resolved) {
448           if (!silc_idlist_find_client_by_id(server->local_list,
449                                              id, TRUE, NULL)) {
450             if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
451                 !silc_idlist_find_client_by_id(server->global_list,
452                                                id, TRUE, NULL)) {
453               silc_server_query_send_router(server, query);
454               for (i = 0; i < query->ids_count; i++)
455                 silc_free(query->ids[i].id);
456               silc_free(query->ids);
457               query->ids = NULL;
458               query->ids_count = 0;
459               silc_free(id);
460               return;
461             }
462           }
463         }
464 
465         query->ids[query->ids_count].id = id;
466         query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
467         query->ids_count++;
468       }
469     }
470 
471     /* Get the max count of reply messages allowed */
472     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
473     if (tmp && tmp_len == sizeof(SilcUInt32))
474       SILC_GET32_MSB(query->reply_count, tmp);
475    break;
476 
477   case SILC_COMMAND_WHOWAS:
478     /* Get nickname */
479     tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
480     if (!tmp) {
481       silc_server_query_send_error(server, query,
482                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
483       silc_server_query_free(query);
484       return;
485     }
486 
487     /* Get the nickname@server string and parse it */
488     if (tmp_len > 128 ||
489         !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
490       silc_server_query_send_error(server, query,
491                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
492       silc_server_query_free(query);
493       return;
494     }
495 
496     /* Check nickname */
497     tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
498                                 SILC_STRING_UTF8, 128, &tmp_len);
499     if (!tmp) {
500       silc_server_query_send_error(server, query,
501                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
502       silc_server_query_free(query);
503       return;
504     }
505     silc_free(query->nickname);
506     query->nickname = tmp;
507 
508     /* Get the max count of reply messages allowed */
509     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
510     if (tmp && tmp_len == sizeof(SilcUInt32))
511       SILC_GET32_MSB(query->reply_count, tmp);
512     break;
513 
514   case SILC_COMMAND_IDENTIFY:
515     /* Get IDs if present. Take IDs always instead of names. */
516     tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
517     if (!tmp) {
518 
519       /* Try get nickname */
520       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
521       if (tmp) {
522         /* Get the nickname@server string and parse it */
523         if (tmp_len > 128 ||
524             !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
525           silc_server_query_add_error(server, query, 1, 1,
526                                       SILC_STATUS_ERR_BAD_NICKNAME);
527 
528         /* Check nickname */
529         tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
530                                     SILC_STRING_UTF8, 128, &tmp_len);
531         if (!tmp) {
532           silc_server_query_send_error(server, query,
533                                        SILC_STATUS_ERR_BAD_NICKNAME, 0);
534           silc_server_query_free(query);
535           return;
536         }
537         silc_free(query->nickname);
538         query->nickname = tmp;
539       }
540 
541       /* Try get server name */
542       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
543       if (tmp) {
544         /* Check server name */
545         tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
546                                     256, &tmp_len);
547         if (!tmp) {
548           silc_server_query_send_error(server, query,
549                                        SILC_STATUS_ERR_BAD_SERVER, 0);
550           silc_server_query_free(query);
551           return;
552         }
553         query->server_name = tmp;
554       }
555 
556       /* Get channel name */
557       tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
558       if (tmp && tmp_len <= 256) {
559         /* Check channel name */
560         tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
561                                     256, &tmp_len);
562         if (!tmp) {
563           silc_server_query_send_error(server, query,
564                                        SILC_STATUS_ERR_BAD_CHANNEL, 0);
565           silc_server_query_free(query);
566           return;
567         }
568         query->channel_name = tmp;
569       }
570 
571       if (!query->nickname && !query->server_name && !query->channel_name) {
572         silc_server_query_send_error(server, query,
573                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
574         silc_server_query_free(query);
575         return;
576       }
577 
578     } else {
579       /* Parse the IDs included in the query */
580       query->ids = silc_calloc(argc, sizeof(*query->ids));
581 
582       for (i = 0; i < argc; i++) {
583         tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
584         if (!tmp)
585           continue;
586 
587         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
588         if (!id) {
589           silc_server_query_add_error(server, query, 1, i + 5,
590                                       SILC_STATUS_ERR_BAD_CLIENT_ID);
591           continue;
592         }
593 
594         /* Normal server must check whether this ID exist, and if not then
595            send the query to router, unless done so already */
596         if (server->server_type == SILC_SERVER && !query->resolved) {
597           if (id_type == SILC_ID_CLIENT) {
598             if (!silc_idlist_find_client_by_id(server->local_list,
599                                                id, TRUE, NULL)) {
600               if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
601                   !silc_idlist_find_client_by_id(server->global_list,
602                                                  id, TRUE, NULL)) {
603                 silc_server_query_send_router(server, query);
604                 for (i = 0; i < query->ids_count; i++)
605                   silc_free(query->ids[i].id);
606                 silc_free(query->ids);
607                 query->ids = NULL;
608                 query->ids_count = 0;
609                 silc_free(id);
610                 return;
611               }
612             }
613           } else {
614             /* For now all other ID's except Client ID's are explicitly
615                sent to router for resolving. */
616             silc_server_query_send_router(server, query);
617             for (i = 0; i < query->ids_count; i++)
618               silc_free(query->ids[i].id);
619             silc_free(query->ids);
620             query->ids = NULL;
621             query->ids_count = 0;
622             silc_free(id);
623             return;
624           }
625         }
626 
627         query->ids[query->ids_count].id = id;
628         query->ids[query->ids_count].id_type = id_type;
629         query->ids_count++;
630       }
631     }
632 
633     /* Get the max count of reply messages allowed */
634     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
635     if (tmp && tmp_len == sizeof(SilcUInt32))
636       SILC_GET32_MSB(query->reply_count, tmp);
637     break;
638   }
639 
640   /* Start processing the query information */
641   silc_server_query_process(server, query, TRUE);
642 }
643 
644 /* Context for holding clients searched by public key. */
645 typedef struct {
646   SilcClientEntry **clients;
647   SilcUInt32 *clients_count;
648   bool found;
649 } *SilcServerPublicKeyUser, SilcServerPublicKeyUserStruct;
650 
651 void silc_server_public_key_hash_foreach(void *key, void *context,
652                                          void *user_context)
653 {
654   SilcServerPublicKeyUser uc = user_context;
655   SilcClientEntry entry = context;
656 
657   /* Nothing was found, just return */
658   if (!context)
659     return;
660 
661   uc->found = TRUE;
662 
663   (*uc->clients) = silc_realloc((*uc->clients),
664                                 sizeof((**uc->clients)) *
665                                 ((*uc->clients_count) + 1));
666   (*uc->clients)[(*uc->clients_count)++] = entry;
667 }
668 
669 /* If clients are set, limit the found clients using the attributes in
670    the query. If clients are not set, try to find some clients using
671    the attributes */
672 
673 void silc_server_query_check_attributes(SilcServer server,
674                                         SilcServerQuery query,
675                                         SilcClientEntry **clients,
676                                         SilcUInt32 *clients_count) {
677   SilcClientEntry entry;
678   SilcAttributePayload attr;
679   SilcAttribute attribute;
680   SilcAttributeObjPk pk;
681   SilcPublicKey publickey;
682   int i;
683   bool found = FALSE, no_clients = FALSE;
684 
685   /* If no clients were found, we only check the attributes
686      if the user wasn't searching for nickname/ids */
687   if (!(*clients)) {
688     no_clients = TRUE;
689     if (query->nickname || query->ids_count)
690       return;
691   }
692 
693   silc_dlist_start(query->attrs);
694   while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
695     attribute = silc_attribute_get_attribute(attr);
696     switch (attribute) {
697 
698       case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
699         SILC_LOG_DEBUG(("Finding clients by public key attribute"));
700 
701         if (!silc_attribute_get_object(attr, &pk, sizeof(pk)))
702           continue;
703 
704         if (!silc_pkcs_public_key_decode(pk.data, pk.data_len,
705                                          &publickey)) {
706           silc_free(pk.type);
707           silc_free(pk.data);
708           continue;
709         }
710 
711         /* If no clients were set on calling this function, we
712            just search for clients, otherwise we try to limit
713            the clients */
714         if (no_clients) {
715           SilcServerPublicKeyUserStruct usercontext;
716 
717           usercontext.clients = clients;
718           usercontext.clients_count = clients_count;
719           usercontext.found = FALSE;
720 
721           silc_hash_table_find_foreach(server->pk_hash, publickey,
722                                        silc_server_public_key_hash_foreach,
723                                        &usercontext);
724 
725           if (usercontext.found == TRUE)
726             found = TRUE;
727         } else {
728           for (i = 0; i < *clients_count; i++) {
729             entry = (*clients)[i];
730 
731             if (!entry->data.public_key)
732               continue;
733 
734             if (!silc_hash_table_find_by_context(server->pk_hash, publickey,
735                                                  entry, NULL))
736               (*clients)[i] = NULL;
737             else
738               found = TRUE;
739           }
740         }
741         silc_free(pk.type);
742         silc_free(pk.data);
743         silc_pkcs_public_key_free(publickey);
744         break;
745     }
746   }
747 
748   if (!found && !query->nickname && !query->ids)
749     silc_server_query_add_error(server, query, 2, 0,
750                                 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
751 }
752 
753 /* Processes the parsed query.  This does the actual finding of the
754    queried information and prepares for sending reply to the original
755    sender of the query command. */
756 
757 void silc_server_query_process(SilcServer server, SilcServerQuery query,
758                                bool resolve)
759 {
760   SilcServerCommandContext cmd = query->cmd;
761   bool check_global = FALSE;
762   void *entry;
763   SilcClientEntry *clients = NULL, client_entry;
764   SilcChannelEntry *channels = NULL;
765   SilcServerEntry *servers = NULL;
766   SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
767   int i;
768 
769   SILC_LOG_DEBUG(("Processing %s query",
770                   silc_get_command_name(query->querycmd)));
771 
772   /* Check global lists if query is coming from client or we are not
773      normal server (we know global information). */
774   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
775     check_global = TRUE;
776   else if (server->server_type != SILC_SERVER)
777     check_global = TRUE;
778 
779   if (query->nickname) {
780     /* Get all clients matching nickname from local list */
781     if (!silc_idlist_get_clients_by_hash(server->local_list,
782                                          query->nickname, server->md5hash,
783                                          &clients, &clients_count))
784       silc_idlist_get_clients_by_nickname(server->local_list,
785                                           query->nickname,
786                                           query->nick_server,
787                                           &clients, &clients_count);
788 
789     /* Check global list as well */
790     if (check_global) {
791       if (!silc_idlist_get_clients_by_hash(server->global_list,
792                                            query->nickname, server->md5hash,
793                                            &clients, &clients_count))
794         silc_idlist_get_clients_by_nickname(server->global_list,
795                                             query->nickname,
796                                             query->nick_server,
797                                             &clients, &clients_count);
798     }
799 
800     if (!clients)
801       silc_server_query_add_error(server, query, 1, 1,
802                                   SILC_STATUS_ERR_NO_SUCH_NICK);
803   }
804 
805   if (query->server_name) {
806     /* Find server by name */
807     entry = silc_idlist_find_server_by_name(server->local_list,
808                                             query->server_name, TRUE, NULL);
809     if (!entry && check_global)
810       entry = silc_idlist_find_server_by_name(server->global_list,
811                                               query->server_name, TRUE, NULL);
812     if (entry) {
813       servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
814       servers[servers_count++] = (SilcServerEntry)entry;
815     }
816 
817     if (!servers)
818       silc_server_query_add_error(server, query, 1, 2,
819                                   SILC_STATUS_ERR_NO_SUCH_SERVER);
820   }
821 
822   if (query->channel_name) {
823     /* Find channel by name */
824     entry = silc_idlist_find_channel_by_name(server->local_list,
825                                              query->channel_name, NULL);
826     if (!entry && check_global)
827       entry = silc_idlist_find_channel_by_name(server->global_list,
828                                                query->channel_name, NULL);
829     if (entry) {
830       channels = silc_realloc(channels, sizeof(*channels) *
831                               (channels_count + 1));
832       channels[channels_count++] = (SilcChannelEntry)entry;
833     }
834 
835     if (!channels)
836       silc_server_query_add_error(server, query, 1, 3,
837                                   SILC_STATUS_ERR_NO_SUCH_CHANNEL);
838   }
839 
840   if (query->ids_count) {
841     /* Find entries by the queried IDs */
842     for (i = 0; i < query->ids_count; i++) {
843       void *id = query->ids[i].id;
844       if (!id)
845         continue;
846 
847       switch (query->ids[i].id_type) {
848 
849       case SILC_ID_CLIENT:
850         /* Get client entry */
851         entry = silc_idlist_find_client_by_id(server->local_list,
852                                               id, TRUE, NULL);
853         if (!entry && check_global)
854           entry = silc_idlist_find_client_by_id(server->global_list,
855                                                 id, TRUE, NULL);
856         if (!entry) {
857           silc_server_query_add_error(server, query, 0, i,
858                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
859           continue;
860         }
861 
862         clients = silc_realloc(clients, sizeof(*clients) *
863                                (clients_count + 1));
864         clients[clients_count++] = (SilcClientEntry)entry;
865         break;
866 
867       case SILC_ID_SERVER:
868         /* Get server entry */
869         entry = silc_idlist_find_server_by_id(server->local_list,
870                                               id, TRUE, NULL);
871         if (!entry && check_global)
872           entry = silc_idlist_find_server_by_id(server->global_list,
873                                                 id, TRUE, NULL);
874         if (!entry) {
875           silc_server_query_add_error(server, query, 0, i,
876                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
877           continue;
878         }
879 
880         servers = silc_realloc(servers, sizeof(*servers) *
881                                (servers_count + 1));
882         servers[servers_count++] = (SilcServerEntry)entry;
883         break;
884 
885       case SILC_ID_CHANNEL:
886         /* Get channel entry */
887         entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
888         if (!entry && check_global)
889           entry = silc_idlist_find_channel_by_id(server->global_list, id,
890                                                  NULL);
891         if (!entry) {
892           silc_server_query_add_error(server, query, 0, i,
893                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
894           continue;
895         }
896 
897         channels = silc_realloc(channels, sizeof(*channels) *
898                                 (channels_count + 1));
899         channels[channels_count++] = (SilcChannelEntry)entry;
900         break;
901 
902       default:
903         break;
904       }
905     }
906   }
907 
908   /* Check the attributes to narrow down the search by using them. */
909   if (query->attrs)
910     silc_server_query_check_attributes(server, query, &clients,
911                                        &clients_count);
912 
913   SILC_LOG_DEBUG(("Querying %d clients", clients_count));
914   SILC_LOG_DEBUG(("Querying %d servers", servers_count));
915   SILC_LOG_DEBUG(("Querying %d channels", channels_count));
916 
917   /* If nothing was found, then just send the errors */
918   if (!clients && !channels && !servers) {
919     silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
920     return;
921   }
922 
923   /* If caller does not want us to resolve anything (has resolved already)
924      then just continue with sending the reply */
925   if (!resolve) {
926     silc_server_query_send_reply(server, query, clients, clients_count,
927                                  servers, servers_count, channels,
928                                  channels_count);
929     silc_free(clients);
930     silc_free(servers);
931     silc_free(channels);
932     return;
933   }
934 
935   /* Now process all found information and if necessary do some more
936      resolving. */
937   switch (query->querycmd) {
938 
939   case SILC_COMMAND_WHOIS:
940     for (i = 0; i < clients_count; i++) {
941       client_entry = clients[i];
942 
943       /* Check if cannot query this anyway, so take next one */
944       if (!client_entry ||
945           !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
946         continue;
947 
948       /* If Requested Attributes is set then we always resolve the client
949          information, if not then check whether the entry is complete or not
950          and decide whether we need to resolve or not. */
951       if (!query->attrs) {
952 
953         /* Even if nickname and stuff are present, we may need to resolve
954            the entry */
955         if (client_entry->nickname && client_entry->username &&
956             client_entry->userinfo) {
957           /* Check if cannot query this anyway, so take next one */
958           if (!client_entry->router)
959             continue;
960 
961           /* If we are router, client is local to us, or client is on channel
962              we do not need to resolve the client information. */
963           if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
964               || silc_hash_table_count(client_entry->channels) ||
965               query->resolved)
966             continue;
967         }
968       }
969 
970       /* Remove the NOATTR status periodically */
971       if (client_entry->data.status & SILC_IDLIST_STATUS_NOATTR &&
972           client_entry->updated + 600 < time(NULL))
973         client_entry->data.status &= ~SILC_IDLIST_STATUS_NOATTR;
974 
975       /* When requested attributes is present and local client is detached
976          we cannot send the command to the client, we'll reply on behalf of
977          the client instead. */
978       if (query->attrs && SILC_IS_LOCAL(client_entry) &&
979           (client_entry->mode & SILC_UMODE_DETACHED ||
980            client_entry->data.status & SILC_IDLIST_STATUS_NOATTR))
981         continue;
982 
983       /* If attributes are present in query, and in the entry and we have
984          done resolvings already we don't need to resolve anymore */
985       if (query->resolved && query->attrs && client_entry->attrs)
986         continue;
987 
988       /* Resolve the detailed client information. If client is local we
989          know that attributes were present and we will resolve directly
990          from the client. Otherwise resolve from client's owner. */
991       silc_server_query_resolve(server, query,
992                                 (SILC_IS_LOCAL(client_entry) ?
993                                  client_entry->connection :
994                                  client_entry->router->connection),
995                                 client_entry);
996     }
997     break;
998 
999   case SILC_COMMAND_WHOWAS:
1000     for (i = 0; i < clients_count; i++) {
1001       client_entry = clients[i];
1002 
1003       /* Check if cannot query this anyway, so take next one */
1004       if (!client_entry || !client_entry->router ||