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 ||