Merge commit 'origin/silc.1.1.branch'
[silc.git] / apps / silcd / server_query.c
index f3696c37a47a78a54f8b3d27a4b267eaf7c0db5d..59eda8024e40cb5d513ef8bc89656311bb9b7150 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  server_query.c 
+  server_query.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 Pekka Riikonen
+  Copyright (C) 2002 - 2005, 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 */
 /* $Id$ */
 
-/* XXX TODO Requested Attributes to WHOIS
-   (NOTE: entry that is incomplete must be resolved first before resolving
-   the attributes) */
-
 #include "serverincludes.h"
 #include "server_internal.h"
 
 typedef struct {
-  SilcSocketConnection sock;       /* Connection of this query */
+  SilcPacketStream sock;           /* Connection of this query */
   unsigned char **arg;             /* Query argument */
   SilcUInt32 *arg_lens;                    /* Query argument lengths */
   SilcUInt32 *arg_types;           /* Query argument types */
@@ -45,26 +41,27 @@ typedef struct {
 /* Represents one error occurred during query */
 typedef struct {
   void *id;                        /* ID */
-  SilcUInt16 index;                /* Index to IDs */
   SilcIdType id_type;              /* ID type */
-  unsigned int from_cmd : 1;               /* TRUE if `index' is from command args,
-                                      otherwise from query->ids */
+  unsigned int index : 15;         /* Index to IDs */
+  unsigned int type : 2;           /* 0 = take from query->ids, 1 = take
+                                      from args, 2 = no args in error. */
   unsigned int error : 7;          /* The actual error (SilcStatus) */
 } *SilcServerQueryError;
 
 /* Query session context */
 typedef struct {
   /* Queried data */
-  char *nickname;                  /* Queried nickname */
-  char *nick_server;               /* Queried nickname's server */
-  char *server_name;               /* Queried server name */
-  char *channel_name;              /* Queried channel name */
+  char nickname[128 + 1];          /* Queried nickname, normalized */
+  char nick_server[128 + 1];       /* Queried nickname's server */
+  char *server_name;               /* Queried server name, normalized */
+  char *channel_name;              /* Queried channel name, normalized */
   SilcServerQueryID ids;           /* Queried IDs */
   SilcUInt32 ids_count;                    /* number of queried IDs */
   SilcUInt32 reply_count;          /* Requested reply count */
   SilcDList attrs;                 /* Requested Attributes in WHOIS */
 
   /* Query session data */
+  SilcPacketStream router;         /* Router to send our query */
   SilcServerCommandContext cmd;            /* Command context for query */
   SilcServerQueryList querylist;    /* Temporary query list context */
   SilcServerQueryID queries;       /* Ongoing queries */
@@ -73,18 +70,23 @@ typedef struct {
   SilcUInt16 queries_count;        /* Number of ongoing queries */
   SilcUInt16 queries_left;         /* Number of ongoing queries left */
   SilcUInt16 errors_count;         /* number of errors */
-  unsigned int querycmd : 7;       /* Query command (SilcCommand) */
-  unsigned int resolved : 1;       /* TRUE if normal server has resolved
+  unsigned int querycmd      : 7;   /* Query command (SilcCommand) */
+  unsigned int resolved      : 1;   /* TRUE if normal server has resolved
                                       information from router */
+  unsigned int dynamic_prim  : 1;   /* Dynamic connection attempt to primary */
+  unsigned int dynamic_retry : 1;   /* Primary returned error, send to
+                                      nick@serv server. */
+  unsigned int parsed        : 1;   /* Set when query is parsed */
 } *SilcServerQuery;
 
+
 void silc_server_query_free(SilcServerQuery query);
 void silc_server_query_send_error(SilcServer server,
                                  SilcServerQuery query,
                                  SilcStatus error, ...);
 void silc_server_query_add_error(SilcServer server,
                                 SilcServerQuery query,
-                                bool from_cmd,
+                                SilcUInt32 type,
                                 SilcUInt32 index,
                                 SilcStatus error);
 void silc_server_query_add_error_id(SilcServer server,
@@ -93,11 +95,12 @@ void silc_server_query_add_error_id(SilcServer server,
                                    void *id, SilcIdType id_type);
 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
 void silc_server_query_send_router_reply(void *context, void *reply);
-void silc_server_query_parse(SilcServer server, SilcServerQuery query);
+SilcBool silc_server_query_parse(SilcServer server, SilcServerQuery query,
+                                SilcBool parse_only);
 void silc_server_query_process(SilcServer server, SilcServerQuery query,
-                              bool resolve);
+                              SilcBool resolve);
 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
-                              SilcSocketConnection sock,
+                              SilcPacketStream sock,
                               SilcClientEntry client_entry);
 void silc_server_query_resolve_reply(void *context, void *reply);
 void silc_server_query_send_reply(SilcServer server,
@@ -108,6 +111,9 @@ void silc_server_query_send_reply(SilcServer server,
                                  SilcUInt32 servers_count,
                                  SilcChannelEntry *channels,
                                  SilcUInt32 channels_count);
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+                                        SilcServerQuery query,
+                                        SilcClientEntry client_entry);
 
 /* Free the query context structure and all allocated resources. */
 
@@ -121,8 +127,6 @@ void silc_server_query_free(SilcServerQuery query)
     silc_free(query->queries[i].id);
   silc_free(query->queries);
 
-  silc_free(query->nickname);
-  silc_free(query->nick_server);
   silc_free(query->server_name);
   silc_free(query->channel_name);
 
@@ -164,7 +168,7 @@ void silc_server_query_send_error(SilcServer server,
 
   /* Send the command reply with error */
   silc_server_send_command_reply(server, query->cmd->sock,
-                                query->querycmd, error, 0, 
+                                query->querycmd, error, 0,
                                 silc_command_get_ident(query->cmd->payload),
                                 argc, data_type, data, data_len);
   va_end(va);
@@ -174,11 +178,13 @@ void silc_server_query_send_error(SilcServer server,
    processing and this function can be used to add one error.  The
    `index' is the index to the command context which includes the argument
    which caused the error, or it is the index to query->ids, depending
-   on value of `from_cmd'. */
+   on value of `type'.  If `type' is 0 the index is to query->ids, if
+   it is 1 it is index to the command context arguments, and if it is
+   2 the index is ignored and no argument is included in the error. */
 
 void silc_server_query_add_error(SilcServer server,
                                 SilcServerQuery query,
-                                bool from_cmd,
+                                SilcUInt32 type,
                                 SilcUInt32 index,
                                 SilcStatus error)
 {
@@ -187,7 +193,7 @@ void silc_server_query_add_error(SilcServer server,
   if (!query->errors)
     return;
   query->errors[query->errors_count].index = index;
-  query->errors[query->errors_count].from_cmd = from_cmd;
+  query->errors[query->errors_count].type = type;
   query->errors[query->errors_count].error = error;
   query->errors[query->errors_count].id = NULL;
   query->errors[query->errors_count].id_type = 0;
@@ -207,7 +213,7 @@ void silc_server_query_add_error_id(SilcServer server,
   if (!query->errors)
     return;
   query->errors[query->errors_count].index = 0;
-  query->errors[query->errors_count].from_cmd = FALSE;
+  query->errors[query->errors_count].type = 0;
   query->errors[query->errors_count].error = error;
   query->errors[query->errors_count].id = silc_id_dup(id, id_type);
   query->errors[query->errors_count].id_type = id_type;
@@ -221,25 +227,37 @@ void silc_server_query_add_error_id(SilcServer server,
    to the entity who sent this query to us automatically.  Returns
    TRUE if the query is being processed or FALSE on error. */
 
-bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
-                              SilcServerCommandContext cmd)
+SilcBool silc_server_query_command(SilcServer server,
+                                  SilcCommand querycmd,
+                                  SilcServerCommandContext cmd,
+                                  void *old_query)
 {
   SilcServerQuery query;
 
   SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd)));
 
-  query = silc_calloc(1, sizeof(*query));
-  query->querycmd = querycmd;
-  query->cmd = silc_server_command_dup(cmd);
+  if (!old_query) {
+    query = silc_calloc(1, sizeof(*query));
+    query->querycmd = querycmd;
+    query->cmd = silc_server_command_dup(cmd);
+    query->router = SILC_PRIMARY_ROUTE(server);
+  } else
+    query = old_query;
 
   switch (querycmd) {
 
   case SILC_COMMAND_WHOIS:
-    /* If we are normal server and query contains nickname, send it
-       directly to router. */
-    if (server->server_type == SILC_SERVER && !server->standalone &&
+    /* If we are normal server and query contains nickname OR query
+       doesn't contain nickname or ids BUT attributes, send it to the
+       router */
+    if (server->server_type != SILC_ROUTER && !server->standalone &&
        cmd->sock != SILC_PRIMARY_ROUTE(server) &&
-       silc_argument_get_arg_type(cmd->args, 1, NULL)) {
+        (silc_argument_get_arg_type(cmd->args, 1, NULL) ||
+        (!silc_argument_get_arg_type(cmd->args, 1, NULL) &&
+         !silc_argument_get_arg_type(cmd->args, 4, NULL) &&
+         silc_argument_get_arg_type(cmd->args, 3, NULL)))) {
+      if (!silc_server_query_parse(server, query, TRUE))
+       return FALSE;
       silc_server_query_send_router(server, query);
       return TRUE;
     }
@@ -261,6 +279,8 @@ bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
     if (server->server_type == SILC_SERVER && !server->standalone &&
        cmd->sock != SILC_PRIMARY_ROUTE(server) &&
        !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
+      if (!silc_server_query_parse(server, query, TRUE))
+       return FALSE;
       silc_server_query_send_router(server, query);
       return TRUE;
     }
@@ -273,11 +293,50 @@ bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
   }
 
   /* Now parse the request */
-  silc_server_query_parse(server, query);
+  silc_server_query_parse(server, query, FALSE);
 
   return TRUE;
 }
 
+/* Remote server connected callback. */
+
+void silc_server_query_connected(SilcServer server,
+                                SilcServerEntry server_entry,
+                                void *context)
+{
+  SilcServerQuery query = context;
+
+  if (!server_entry) {
+    /* Connecting failed */
+    SilcConnectionType type = (server->server_type == SILC_ROUTER ?
+                              SILC_CONN_SERVER : SILC_CONN_ROUTER);
+
+    if (query->dynamic_prim /* && @serv != prim.host.name */ &&
+       !silc_server_num_sockets_by_remote(server, query->nick_server,
+                                          query->nick_server, 706, type)) {
+      /* Connection attempt to primary router failed, now try to the one
+        specified in nick@server. */
+      silc_server_create_connection(server, FALSE, TRUE, query->nick_server,
+                                   706, silc_server_query_connected,
+                                   query);
+      query->dynamic_prim = FALSE;
+      return;
+    }
+
+    /* Process the query after failed connect.  This will send error back
+       because such nick was not found. */
+    SILC_LOG_DEBUG(("Process query, connecting failed"));
+    silc_server_query_process(server, query, TRUE);
+    return;
+  }
+
+  /* Reprocess the query */
+  SILC_LOG_DEBUG(("Reprocess query after creating connection to %s",
+                 server_entry->server_name));
+  query->router = server_entry->data.sconn->sock;
+  silc_server_query_command(server, query->querycmd, query->cmd, query);
+}
+
 /* Send the received query to our primary router since we could not
    handle the query directly.  We will reprocess the query after our
    router replies back. */
@@ -287,16 +346,19 @@ void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
   SilcBuffer tmpbuf;
   SilcUInt16 old_ident;
 
-  SILC_LOG_DEBUG(("Forwarding the query to router for processing"));
+  SILC_LOG_DEBUG(("Forwarding the query to router %p for processing",
+                 query->router));
+
+  /* Statistics */
+  server->stat.commands_sent++;
 
   /* Send WHOIS command to our router */
   old_ident = silc_command_get_ident(query->cmd->payload);
   silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
-  silc_server_packet_send(server, 
-                         SILC_PRIMARY_ROUTE(server),
+  silc_server_packet_send(server, query->router,
                          SILC_PACKET_COMMAND, 0,
-                         tmpbuf->data, tmpbuf->len, TRUE);
+                         tmpbuf->data, silc_buffer_len(tmpbuf));
   silc_command_set_ident(query->cmd->payload, old_ident);
   silc_buffer_free(tmpbuf);
 
@@ -318,65 +380,128 @@ void silc_server_query_send_router_reply(void *context, void *reply)
 
   SILC_LOG_DEBUG(("Received reply from router to query"));
 
+  /* If the original command caller has gone away, just stop. */
+  if (!silc_packet_stream_is_valid(query->cmd->sock)) {
+    SILC_LOG_DEBUG(("Original command caller vanished"));
+    silc_server_query_free(query);
+    return;
+  }
+
   /* Check if router sent error reply */
   if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
     SilcBuffer buffer;
+    SilcConnectionType type = (server->server_type == SILC_ROUTER ?
+                              SILC_CONN_SERVER : SILC_CONN_ROUTER);
+
+    /* If this was nick@server query, retry to @serv if the primary router
+       returned error. */
+    if (query->nick_server[0] && !query->dynamic_retry &&
+       !silc_server_num_sockets_by_remote(server, query->nick_server,
+                                          query->nick_server, 706, type)) {
+      SILC_LOG_DEBUG(("Retry query by connecting to %s:%d",
+                     query->nick_server, 706));
+      silc_server_create_connection(server, FALSE, TRUE, query->nick_server,
+                                   706, silc_server_query_connected,
+                                   query);
+      query->dynamic_retry = TRUE;
+      query->resolved = FALSE;
+      return;
+    }
 
     SILC_LOG_DEBUG(("Sending error to original query"));
 
+    /* Statistics */
+    server->stat.commands_sent++;
+
     /* Send the same command reply payload which contains the error */
     silc_command_set_command(cmdr->payload, query->querycmd);
     silc_command_set_ident(cmdr->payload,
                           silc_command_get_ident(query->cmd->payload));
     buffer = silc_command_payload_encode_payload(cmdr->payload);
     silc_server_packet_send(server, query->cmd->sock,
-                           SILC_PACKET_COMMAND_REPLY, 0, 
-                           buffer->data, buffer->len, FALSE);
+                           SILC_PACKET_COMMAND_REPLY, 0,
+                           buffer->data, silc_buffer_len(buffer));
     silc_buffer_free(buffer);
     silc_server_query_free(query);
     return;
   }
 
   /* Continue with parsing */
-  silc_server_query_parse(server, query);
+  silc_server_query_parse(server, query, FALSE);
 }
 
 /* Parse the command query and start processing the queries in detail. */
 
-void silc_server_query_parse(SilcServer server, SilcServerQuery query)
+SilcBool silc_server_query_parse(SilcServer server, SilcServerQuery query,
+                                SilcBool parse_only)
 {
   SilcServerCommandContext cmd = query->cmd;
+  SilcIDListData idata = silc_packet_get_context(cmd->sock);
   unsigned char *tmp;
   SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
-  void *id;
-  SilcIdType id_type;
+  SilcID id;
   int i;
 
   SILC_LOG_DEBUG(("Parsing %s query",
                  silc_get_command_name(query->querycmd)));
 
+  if (query->parsed)
+    goto parsed;
+
   switch (query->querycmd) {
 
   case SILC_COMMAND_WHOIS:
+    /* Get requested attributes if set */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+    if (tmp && !query->attrs && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN) {
+      query->attrs = silc_attribute_payload_parse(tmp, tmp_len);
+
+      /* When Requested Attributes is present we will assure that this
+        client cannot execute the WHOIS command too fast.  This would be
+        same as having SILC_CF_LAG_STRICT. */
+      if (idata && idata->conn_type == SILC_CONN_CLIENT)
+       ((SilcClientEntry)idata)->fast_command = 6;
+    }
+
     /* Get Client IDs if present. Take IDs always instead of nickname. */
     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
     if (!tmp) {
 
       /* Get nickname */
       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-      if (!tmp) {
+      if (!tmp && !query->attrs) {
+       /* No nickname, no ids and no attributes - send error */
        silc_server_query_send_error(server, query,
                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        silc_server_query_free(query);
-       return;
+       return FALSE;
       }
 
       /* Get the nickname@server string and parse it */
-      if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
+      if (tmp && ((tmp_len > 128) ||
+                 !silc_parse_userfqdn(tmp, query->nickname,
+                                      sizeof(query->nickname),
+                                      query->nick_server,
+                                      sizeof(query->nick_server)))) {
        silc_server_query_send_error(server, query,
                                     SILC_STATUS_ERR_BAD_NICKNAME, 0);
        silc_server_query_free(query);
-       return;
+       return FALSE;
+      }
+
+      /* Check nickname */
+      if (tmp) {
+       tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                                   SILC_STRING_UTF8, 128, &tmp_len);
+       if (!tmp) {
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
+         silc_server_query_free(query);
+         return FALSE;
+       }
+       memset(query->nickname, 0, sizeof(query->nickname));
+       silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp);
+       silc_free(tmp);
       }
 
     } else {
@@ -388,9 +513,9 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        if (!tmp)
          continue;
 
-       id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-       if (!id) {
-         silc_server_query_add_error(server, query, TRUE, i + 4,
+       if (!silc_id_payload_parse_id(tmp, tmp_len, &id) ||
+           id.type != SILC_ID_CLIENT) {
+         silc_server_query_add_error(server, query, 1, i + 4,
                                      SILC_STATUS_ERR_BAD_CLIENT_ID);
          continue;
        }
@@ -399,20 +524,23 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
           send the query to router, unless done so already */
        if (server->server_type == SILC_SERVER && !query->resolved) {
          if (!silc_idlist_find_client_by_id(server->local_list,
-                                            id, TRUE, NULL)) {
-           if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
+                                            &id.u.client_id, TRUE, NULL)) {
+           if (idata->conn_type != SILC_CONN_CLIENT ||
                !silc_idlist_find_client_by_id(server->global_list,
-                                              id, TRUE, NULL)) {
+                                              &id.u.client_id, TRUE, NULL)) {
              silc_server_query_send_router(server, query);
              for (i = 0; i < query->ids_count; i++)
                silc_free(query->ids[i].id);
              silc_free(query->ids);
-             return;
+             query->ids = NULL;
+             query->ids_count = 0;
+             return FALSE;
            }
          }
        }
 
-       query->ids[query->ids_count].id = id;
+       query->ids[query->ids_count].id = silc_id_dup(&id.u.client_id,
+                                                     SILC_ID_CLIENT);
        query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
        query->ids_count++;
       }
@@ -422,12 +550,7 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
     if (tmp && tmp_len == sizeof(SilcUInt32))
       SILC_GET32_MSB(query->reply_count, tmp);
-
-    /* Get requested attributes if set */
-    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-    if (tmp)
-      query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
-    break;
+   break;
 
   case SILC_COMMAND_WHOWAS:
     /* Get nickname */
@@ -436,17 +559,32 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
       silc_server_query_send_error(server, query,
                                   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
       silc_server_query_free(query);
-      return;
+      return FALSE;
     }
 
     /* Get the nickname@server string and parse it */
-    if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
+    if (tmp_len > 128 ||
+       !silc_parse_userfqdn(tmp, query->nickname, sizeof(query->nickname),
+                            query->nick_server, sizeof(query->nick_server))) {
       silc_server_query_send_error(server, query,
                                   SILC_STATUS_ERR_BAD_NICKNAME, 0);
       silc_server_query_free(query);
-      return;
+      return FALSE;
     }
 
+    /* Check nickname */
+    tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                               SILC_STRING_UTF8, 128, &tmp_len);
+    if (!tmp) {
+      silc_server_query_send_error(server, query,
+                                  SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      silc_server_query_free(query);
+      return FALSE;
+    }
+    memset(query->nickname, 0, sizeof(query->nickname));
+    silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp);
+    silc_free(tmp);
+
     /* Get the max count of reply messages allowed */
     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
     if (tmp && tmp_len == sizeof(SilcUInt32))
@@ -462,20 +600,64 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
       if (tmp) {
        /* Get the nickname@server string and parse it */
-       if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
-         silc_server_query_add_error(server, query, TRUE, 1,
+       if (tmp_len > 128 ||
+           !silc_parse_userfqdn(tmp, query->nickname,
+                                sizeof(query->nickname),
+                                query->nick_server,
+                                sizeof(query->nick_server)))
+         silc_server_query_add_error(server, query, 1, 1,
                                      SILC_STATUS_ERR_BAD_NICKNAME);
+
+       /* Check nickname */
+       tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                                   SILC_STRING_UTF8, 128, &tmp_len);
+       if (!tmp) {
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
+         silc_server_query_free(query);
+         return FALSE;
+       }
+       memset(query->nickname, 0, sizeof(query->nickname));
+       silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp);
+       silc_free(tmp);
       }
 
       /* Try get server name */
       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-      if (tmp)
-       query->server_name = silc_memdup(tmp, tmp_len);
+      if (tmp) {
+       /* Check server name */
+       tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
+                                   256, &tmp_len);
+       if (!tmp) {
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_SERVER, 0);
+         silc_server_query_free(query);
+         return FALSE;
+       }
+       query->server_name = tmp;
+      }
 
       /* Get channel name */
       tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-      if (tmp)
-       query->channel_name = silc_memdup(tmp, tmp_len);
+      if (tmp && tmp_len <= 256) {
+       /* Check channel name */
+       tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
+                                   256, &tmp_len);
+       if (!tmp) {
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_CHANNEL, 0);
+         silc_server_query_free(query);
+         return FALSE;
+       }
+       query->channel_name = tmp;
+      }
+
+      if (!query->nickname[0] && !query->server_name && !query->channel_name) {
+       silc_server_query_send_error(server, query,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       silc_server_query_free(query);
+       return FALSE;
+      }
 
     } else {
       /* Parse the IDs included in the query */
@@ -486,32 +668,54 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        if (!tmp)
          continue;
 
-       id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
-       if (!id) {
-         silc_server_query_add_error(server, query, TRUE, i + 5,
-                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) {
+         silc_server_query_add_error(server, query, 1, i + 5,
+                                     SILC_STATUS_ERR_BAD_CLIENT_ID);
          continue;
        }
 
        /* Normal server must check whether this ID exist, and if not then
           send the query to router, unless done so already */
        if (server->server_type == SILC_SERVER && !query->resolved) {
-         if (!silc_idlist_find_client_by_id(server->local_list,
-                                            id, TRUE, NULL)) {
-           if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
-               !silc_idlist_find_client_by_id(server->global_list,
-                                              id, TRUE, NULL)) {
-             silc_server_query_send_router(server, query);
-             for (i = 0; i < query->ids_count; i++)
-               silc_free(query->ids[i].id);
-             silc_free(query->ids);
-             return;
+         if (id.type == SILC_ID_CLIENT) {
+           if (!silc_idlist_find_client_by_id(server->local_list,
+                                              &id.u.client_id, TRUE, NULL)) {
+             if (idata->conn_type != SILC_CONN_CLIENT ||
+                 !silc_idlist_find_client_by_id(server->global_list,
+                                                &id.u.client_id, TRUE,
+                                                NULL)) {
+               silc_server_query_send_router(server, query);
+               for (i = 0; i < query->ids_count; i++)
+                 silc_free(query->ids[i].id);
+               silc_free(query->ids);
+               query->ids = NULL;
+               query->ids_count = 0;
+               return FALSE;
+             }
            }
+         } else {
+           /* For now all other ID's except Client ID's are explicitly
+              sent to router for resolving. */
+           silc_server_query_send_router(server, query);
+           for (i = 0; i < query->ids_count; i++)
+             silc_free(query->ids[i].id);
+           silc_free(query->ids);
+           query->ids = NULL;
+           query->ids_count = 0;
+           return FALSE;
          }
        }
 
-       query->ids[query->ids_count].id = id;
-       query->ids[query->ids_count].id_type = id_type;
+       if (id.type == SILC_ID_CLIENT)
+         query->ids[query->ids_count].id = silc_id_dup(&id.u.client_id,
+                                                       SILC_ID_CLIENT);
+       if (id.type == SILC_ID_SERVER)
+         query->ids[query->ids_count].id = silc_id_dup(&id.u.server_id,
+                                                       SILC_ID_SERVER);
+       if (id.type == SILC_ID_CHANNEL)
+         query->ids[query->ids_count].id = silc_id_dup(&id.u.channel_id,
+                                                       SILC_ID_CHANNEL);
+       query->ids[query->ids_count].id_type = id.type;
        query->ids_count++;
       }
     }
@@ -523,8 +727,197 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
     break;
   }
 
+  query->parsed = TRUE;
+
+ parsed:
+  if (!parse_only && query->nickname) {
+    switch (query->querycmd) {
+    case SILC_COMMAND_WHOIS:
+    case SILC_COMMAND_IDENTIFY:
+      /* Check server name.  If we are open server and don't yet have
+        connection to remote router, create it now. */
+      if (query->nick_server[0] && server->config->dynamic_server &&
+         !query->resolved) {
+       /* If primary router is specified, use that.  Otherwise connect
+          to the server in nick@server string. */
+       SilcServerConfigRouter *router;
+       SilcConnectionType type = (server->server_type == SILC_ROUTER ?
+                                  SILC_CONN_SERVER : SILC_CONN_ROUTER);
+
+       router = silc_server_config_get_primary_router(server);
+       if (router && server->standalone) {
+         /* Create connection to primary router */
+         SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d",
+                         router->host, router->port));
+         query->dynamic_prim = TRUE;
+         silc_server_create_connection(server, FALSE, TRUE,
+                                       router->host, router->port,
+                                       silc_server_query_connected, query);
+         return FALSE;
+       } else if (!silc_server_num_sockets_by_remote(server,
+                                                     query->nick_server,
+                                                     query->nick_server,
+                                                     706, type)) {
+         /* Create connection and handle the query after connection */
+         SILC_LOG_DEBUG(("Create dynamic connection to %s:%d",
+                         query->nick_server, 706));
+         silc_server_create_connection(server, FALSE, TRUE,
+                                       query->nick_server, 706,
+                                       silc_server_query_connected, query);
+         return FALSE;
+       }
+      }
+    }
+  }
+
   /* Start processing the query information */
-  silc_server_query_process(server, query, TRUE);
+  if (!parse_only)
+    silc_server_query_process(server, query, TRUE);
+
+  return TRUE;
+}
+
+/* Context for holding clients searched by public key. */
+typedef struct {
+  SilcClientEntry **clients;
+  SilcUInt32 *clients_count;
+  SilcBool found;
+} *SilcServerPublicKeyUser, SilcServerPublicKeyUserStruct;
+
+/* SKR find callbcak */
+
+static void silc_server_query_skr_callback(SilcSKR skr,
+                                          SilcSKRFind find,
+                                          SilcSKRStatus status,
+                                          SilcDList keys,
+                                          void *context)
+{
+  SilcServerPublicKeyUser uc = context;
+  SilcSKRKey key;
+
+  if (keys) {
+    (*uc->clients) = silc_realloc((*uc->clients),
+                                 sizeof((**uc->clients)) *
+                                 ((*uc->clients_count) +
+                                  silc_dlist_count(keys)));
+
+    silc_dlist_start(keys);
+    while ((key = silc_dlist_get(keys)))
+      (*uc->clients)[(*uc->clients_count)++] = key->key_context;
+
+    uc->found = TRUE;
+    silc_dlist_uninit(keys);
+  }
+
+  silc_skr_find_free(find);
+}
+
+/* If clients are set, limit the found clients using the attributes in
+   the query. If clients are not set, try to find some clients using
+   the attributes */
+
+void silc_server_query_check_attributes(SilcServer server,
+                                        SilcServerQuery query,
+                                        SilcClientEntry **clients,
+                                        SilcUInt32 *clients_count) {
+  SilcClientEntry entry;
+  SilcAttributePayload attr;
+  SilcAttribute attribute;
+  SilcAttributeObjPk pk;
+  SilcPublicKey publickey, cmp_pubkey;
+  SilcPKCSType type;
+  SilcBool found = FALSE, no_clients = FALSE, search_pubkey = FALSE;
+  int i;
+
+  /* If no clients were found, we only check the attributes
+     if the user wasn't searching for nickname/ids */
+  if (!(*clients)) {
+    no_clients = TRUE;
+    if (query->nickname[0] || query->ids_count)
+      return;
+  }
+
+  silc_dlist_start(query->attrs);
+  while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
+    attribute = silc_attribute_get_attribute(attr);
+    switch (attribute) {
+
+      case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+       SILC_LOG_DEBUG(("Finding clients by public key attribute"));
+
+       if (!silc_attribute_get_object(attr, &pk, sizeof(pk)))
+         continue;
+
+       if (!strcmp(pk.type, "silc-rsa"))
+         type = SILC_PKCS_SILC;
+       else if (!strcmp(pk.type, "ssh-rsa"))
+         type = SILC_PKCS_SSH2;
+       else if (!strcmp(pk.type, "x509v3-sign-rsa"))
+         type = SILC_PKCS_X509V3;
+       else if (!strcmp(pk.type, "pgp-sign-rsa"))
+         type = SILC_PKCS_OPENPGP;
+       else
+         continue;
+
+       if (!silc_pkcs_public_key_alloc(type, pk.data, pk.data_len,
+                                       &publickey)) {
+         silc_free(pk.type);
+         silc_free(pk.data);
+         continue;
+       }
+       search_pubkey = TRUE;
+
+       /* If no clients were set on calling this function, we just search
+          for clients, otherwise we try to limit the clients. */
+       if (no_clients) {
+         SilcServerPublicKeyUserStruct usercontext;
+         SilcSKRFind find;
+
+         usercontext.clients = clients;
+         usercontext.clients_count = clients_count;
+         usercontext.found = FALSE;
+
+         find = silc_skr_find_alloc();
+         if (!find)
+           continue;
+
+         silc_skr_find_set_public_key(find, publickey);
+         silc_skr_find_set_usage(find, SILC_SKR_USAGE_IDENTIFICATION);
+         silc_skr_find(server->repository, server->schedule,
+                       find, silc_server_query_skr_callback, &usercontext);
+
+         if (usercontext.found == TRUE)
+           found = TRUE;
+       } else {
+         for (i = 0; i < *clients_count; i++) {
+           entry = (*clients)[i];
+
+           if (!entry->data.public_key)
+             continue;
+
+           if (silc_server_get_public_key_by_client(server, entry,
+                                                    &cmp_pubkey)) {
+             if (silc_pkcs_public_key_compare(cmp_pubkey, publickey)) {
+               found = TRUE;
+               continue;
+             }
+           }
+
+           (*clients)[i] = NULL;
+         }
+       }
+       silc_free(pk.type);
+       silc_free(pk.data);
+       silc_pkcs_public_key_free(publickey);
+       break;
+    }
+  }
+
+  if (!found && !query->nickname[0] && !query->ids)
+    silc_server_query_add_error(server, query, 2, 0,
+                               search_pubkey ?
+                                SILC_STATUS_ERR_NO_SUCH_PUBLIC_KEY :
+                               SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 }
 
 /* Processes the parsed query.  This does the actual finding of the
@@ -532,10 +925,11 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
    sender of the query command. */
 
 void silc_server_query_process(SilcServer server, SilcServerQuery query,
-                              bool resolve)
+                              SilcBool resolve)
 {
   SilcServerCommandContext cmd = query->cmd;
-  bool check_global = FALSE;
+  SilcIDListData idata = silc_packet_get_context(cmd->sock);
+  SilcBool check_global = FALSE;
   void *entry;
   SilcClientEntry *clients = NULL, client_entry;
   SilcChannelEntry *channels = NULL;
@@ -548,34 +942,42 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
 
   /* Check global lists if query is coming from client or we are not
      normal server (we know global information). */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+  if (idata->conn_type == SILC_CONN_CLIENT)
     check_global = TRUE;
   else if (server->server_type != SILC_SERVER)
     check_global = TRUE;
 
-  if (query->nickname) {
+  if (query->nickname[0]) {
     /* Get all clients matching nickname from local list */
-    if (!silc_idlist_get_clients_by_hash(server->local_list, 
-                                        query->nickname, server->md5hash,
+    if (!silc_idlist_get_clients_by_hash(server->local_list,
+                                        query->nickname,
+                                        query->nick_server[0] ?
+                                        query->nick_server : NULL,
+                                        server->md5hash,
                                         &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->local_list, 
+      silc_idlist_get_clients_by_nickname(server->local_list,
                                          query->nickname,
-                                         query->nick_server,
+                                         query->nick_server[0] ?
+                                         query->nick_server : NULL,
                                          &clients, &clients_count);
 
     /* Check global list as well */
     if (check_global) {
-      if (!silc_idlist_get_clients_by_hash(server->global_list, 
-                                          query->nickname, server->md5hash,
+      if (!silc_idlist_get_clients_by_hash(server->global_list,
+                                          query->nickname,
+                                          query->nick_server[0] ?
+                                          query->nick_server : NULL,
+                                          server->md5hash,
                                           &clients, &clients_count))
-       silc_idlist_get_clients_by_nickname(server->global_list, 
+       silc_idlist_get_clients_by_nickname(server->global_list,
                                            query->nickname,
-                                           query->nick_server,
+                                           query->nick_server[0] ?
+                                           query->nick_server : NULL,
                                            &clients, &clients_count);
     }
 
     if (!clients)
-      silc_server_query_add_error(server, query, TRUE, 1,
+      silc_server_query_add_error(server, query, 1, 1,
                                  SILC_STATUS_ERR_NO_SUCH_NICK);
   }
 
@@ -592,7 +994,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
     }
 
     if (!servers)
-      silc_server_query_add_error(server, query, TRUE, 2,
+      silc_server_query_add_error(server, query, 1, 2,
                                  SILC_STATUS_ERR_NO_SUCH_SERVER);
   }
 
@@ -610,7 +1012,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
     }
 
     if (!channels)
-      silc_server_query_add_error(server, query, TRUE, 3,
+      silc_server_query_add_error(server, query, 1, 3,
                                  SILC_STATUS_ERR_NO_SUCH_CHANNEL);
   }
 
@@ -631,7 +1033,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          entry = silc_idlist_find_client_by_id(server->global_list,
                                                id, TRUE, NULL);
        if (!entry) {
-         silc_server_query_add_error(server, query, FALSE, i,
+         silc_server_query_add_error(server, query, 0, i,
                                      SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
          continue;
        }
@@ -649,7 +1051,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          entry = silc_idlist_find_server_by_id(server->global_list,
                                                id, TRUE, NULL);
        if (!entry) {
-         silc_server_query_add_error(server, query, FALSE, i,
+         silc_server_query_add_error(server, query, 0, i,
                                      SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
          continue;
        }
@@ -666,7 +1068,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          entry = silc_idlist_find_channel_by_id(server->global_list, id,
                                                 NULL);
        if (!entry) {
-         silc_server_query_add_error(server, query, FALSE, i,
+         silc_server_query_add_error(server, query, 0, i,
                                      SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
          continue;
        }
@@ -682,6 +1084,15 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
     }
   }
 
+  /* Check the attributes to narrow down the search by using them. */
+  if (query->attrs)
+    silc_server_query_check_attributes(server, query, &clients,
+                                       &clients_count);
+
+  SILC_LOG_DEBUG(("Querying %d clients", clients_count));
+  SILC_LOG_DEBUG(("Querying %d servers", servers_count));
+  SILC_LOG_DEBUG(("Querying %d channels", channels_count));
+
   /* If nothing was found, then just send the errors */
   if (!clients && !channels && !servers) {
     silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
@@ -713,10 +1124,13 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
        continue;
 
-      /* If requested attributes is set then we always resolve the client
+      /* If Requested Attributes is set then we always resolve the client
         information, if not then check whether the entry is complete or not
         and decide whether we need to resolve or not. */
       if (!query->attrs) {
+
+       /* Even if nickname and stuff are present, we may need to resolve
+          the entry */
        if (client_entry->nickname && client_entry->username &&
            client_entry->userinfo) {
          /* Check if cannot query this anyway, so take next one */
@@ -732,6 +1146,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
        }
       }
 
+      /* Remove the NOATTR status periodically */
+      if (client_entry->data.status & SILC_IDLIST_STATUS_NOATTR &&
+         client_entry->updated + 600 < time(NULL))
+       client_entry->data.status &= ~SILC_IDLIST_STATUS_NOATTR;
+
       /* When requested attributes is present and local client is detached
         we cannot send the command to the client, we'll reply on behalf of
         the client instead. */
@@ -740,6 +1159,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
           client_entry->data.status & SILC_IDLIST_STATUS_NOATTR))
        continue;
 
+      /* If attributes are present in query, and in the entry and we have
+        done resolvings already we don't need to resolve anymore */
+      if (query->resolved && query->attrs && client_entry->attrs)
+       continue;
+
       /* Resolve the detailed client information. If client is local we
         know that attributes were present and we will resolve directly
         from the client. Otherwise resolve from client's owner. */
@@ -780,7 +1204,9 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
        continue;
 
+      /* Even if nickname is present, we may need to resolve the entry */
       if (client_entry->nickname) {
+
        /* If we are router, client is local to us, or client is on channel
           we do not need to resolve the client information. */
        if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
@@ -817,7 +1243,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
    client entry calls this function to do the resolving. */
 
 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
-                              SilcSocketConnection sock,
+                              SilcPacketStream sock,
                               SilcClientEntry client_entry)
 {
   SilcServerCommandContext cmd = query->cmd;
@@ -846,12 +1272,30 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
     for (i = 0; i < query->querylist_count; i++) {
       r = &query->querylist[i];
 
+      /* If Requested Attributes were present put them to this resolving */
+      if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
+       len = r->argc + 1;
+       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
+       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
+       r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
+
+       tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+       if (tmp)
+         r->arg[r->argc] = silc_memdup(tmp, len);
+       r->arg_lens[r->argc] = len;
+       r->arg_types[r->argc] = 3;
+       r->argc++;
+      }
+
+      /* Statistics */
+      server->stat.commands_sent++;
+
       /* Send WHOIS command */
       res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
                                            r->argc, r->arg, r->arg_lens,
                                            r->arg_types, r->ident);
       silc_server_packet_send(server, r->sock, SILC_PACKET_COMMAND, 0,
-                             res_cmd->data, res_cmd->len, FALSE);
+                             res_cmd->data, silc_buffer_len(res_cmd));
       silc_buffer_free(res_cmd);
 
       /* Reprocess this packet after received reply */
@@ -895,7 +1339,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
     case SILC_COMMAND_WHOIS:
     case SILC_COMMAND_IDENTIFY:
       /* Take existing query context if exist for this connection */
-      for (i = 0; i < query->queries_count; i++)
+      for (i = 0; i < query->querylist_count; i++)
        if (query->querylist[i].sock == sock) {
          r = &query->querylist[i];
          break;
@@ -904,7 +1348,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
       if (!r) {
        /* Allocate new temp query list context */
        query->querylist = silc_realloc(query->querylist,
-                                       sizeof(*query->querylist) * 
+                                       sizeof(*query->querylist) *
                                        (query->querylist_count + 1));
        r = &query->querylist[query->querylist_count];
        query->querylist_count++;
@@ -915,21 +1359,6 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
          r->timeout = 3;
       }
 
-      /* If attributes were present put them to this resolving */
-      if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
-       len = r->argc + 1;
-       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
-       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
-       r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
-
-       tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-       if (tmp)
-         r->arg[r->argc] = silc_memdup(tmp, len);
-       r->arg_lens[r->argc] = len;
-       r->arg_types[r->argc] = 3;
-       r->argc++;
-      }
-
       len = r->argc + 1;
       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
@@ -937,8 +1366,8 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
 
       /* Add the client entry to be resolved */
       idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
-      r->arg[r->argc] = silc_memdup(idp->data, idp->len);
-      r->arg_lens[r->argc] = idp->len;
+      r->arg[r->argc] = silc_memdup(idp->data, silc_buffer_len(idp));
+      r->arg_lens[r->argc] = silc_buffer_len(idp);
       r->arg_types[r->argc] = r->argc + 4;
       r->argc++;
       silc_buffer_free(idp);
@@ -961,6 +1390,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
   client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
   client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
   client_entry->resolve_cmd_ident = ident;
+  client_entry->updated = time(NULL);
 
   /* Save the queried ID, which we will reprocess after we get this and
      all other queries back. */
@@ -1011,6 +1441,7 @@ void silc_server_query_resolve_reply(void *context, void *reply)
     id = &query->queries[i];
 
     if (error == SILC_STATUS_ERR_TIMEDOUT) {
+
       /* If timeout occurred for local entry when resolving attributes
         mark that this client doesn't support attributes in WHOIS. This
         assures we won't send the request again to the client. */
@@ -1019,8 +1450,22 @@ void silc_server_query_resolve_reply(void *context, void *reply)
                                                     id->id, TRUE, NULL);
        SILC_LOG_DEBUG(("Client %s does not support Requested Attributes",
                        silc_id_render(id->id, SILC_ID_CLIENT)));
-       if (client_entry && SILC_IS_LOCAL(client_entry))
+       if (client_entry && SILC_IS_LOCAL(client_entry)) {
          client_entry->data.status |= SILC_IDLIST_STATUS_NOATTR;
+         client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+          continue;
+       }
+      }
+
+      /* Remove the RESOLVING status from the client entry */
+      if (query->querycmd != SILC_COMMAND_WHOWAS) {
+       client_entry = silc_idlist_find_client_by_id(server->local_list,
+                                                    id->id, TRUE, NULL);
+       if (!client_entry)
+         client_entry = silc_idlist_find_client_by_id(server->global_list,
+                                                      id->id, TRUE, NULL);
+       if (client_entry)
+         client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
       }
     }
   }
@@ -1033,6 +1478,13 @@ void silc_server_query_resolve_reply(void *context, void *reply)
 
   SILC_LOG_DEBUG(("Reprocess the query"));
 
+  /* If the original command caller has gone away, just stop. */
+  if (!silc_packet_stream_is_valid(query->cmd->sock)) {
+    SILC_LOG_DEBUG(("Original command caller vanished"));
+    silc_server_query_free(query);
+    return;
+  }
+
   /* We have received all queries.  Now re-search all information required
      to complete this query.  Reason we cannot save the values found in
      the first search is that SilcClientEntry, SilcServerEntry and
@@ -1060,26 +1512,36 @@ void silc_server_query_send_reply(SilcServer server,
                                  SilcUInt32 channels_count)
 {
   SilcServerCommandContext cmd = query->cmd;
+  SilcIDListData idata = silc_packet_get_context(cmd->sock);
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   SilcStatus status;
   unsigned char *tmp;
+  char *tmp2;
   SilcUInt32 len;
   SilcBuffer idp;
   int i, k, valid_count;
-  char nh[256], uh[256];
+  char nh[384], uh[384];
+  SilcBool sent_reply = FALSE;
 
   SILC_LOG_DEBUG(("Sending reply to query"));
+  SILC_LOG_DEBUG(("Sending %d clients", clients_count));
+  SILC_LOG_DEBUG(("Sending %d servers", servers_count));
+  SILC_LOG_DEBUG(("Sending %d channels", channels_count));
+  SILC_LOG_DEBUG(("Sending %d errors", query->errors_count));
 
   status = SILC_STATUS_OK;
 
   /* Send clients */
   if (clients_count) {
     SilcClientEntry entry;
-    SilcSocketConnection hsock;
+    SilcPacketStream hsock;
 
     /* Mark all invalid entries */
     for (i = 0, valid_count = 0; i < clients_count; i++) {
       entry = clients[i];
+      if (!entry)
+       continue;
+
       switch (query->querycmd) {
       case SILC_COMMAND_WHOIS:
        if (!entry->nickname || !entry->username || !entry->userinfo ||
@@ -1132,7 +1594,7 @@ void silc_server_query_send_reply(SilcServer server,
 
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
-      if (valid_count > 1 && k == valid_count - 1 
+      if (valid_count > 1 && k == valid_count - 1
          && !servers_count && !channels_count && !query->errors_count)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
@@ -1146,7 +1608,6 @@ void silc_server_query_send_reply(SilcServer server,
                       "      : "), entry->nickname));
 
       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-      memset(uh, 0, sizeof(uh));
       memset(nh, 0, sizeof(nh));
 
       silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname));
@@ -1163,32 +1624,36 @@ void silc_server_query_send_reply(SilcServer server,
                       server->server_name, len);
        }
       }
-      
+
       switch (query->querycmd) {
-       
+
       case SILC_COMMAND_WHOIS:
        {
          unsigned char idle[4], mode[4];
-         unsigned char *fingerprint, fempty[20];
-         SilcBuffer channels, umode_list = NULL;
+         unsigned char *fingerprint, fempty[20], *attrs = NULL;
+         SilcBuffer channels, umode_list = NULL, tmpattrs = NULL;
 
          memset(fempty, 0, sizeof(fempty));
+         memset(idle, 0, sizeof(idle));
+         memset(uh, 0, sizeof(uh));
+
          silc_strncat(uh, sizeof(uh), entry->username,
                       strlen(entry->username));
          if (!strchr(entry->username, '@') && entry->connection) {
            hsock = entry->connection;
            silc_strncat(uh, sizeof(uh), "@", 1);
-           len = strlen(hsock->hostname);
-           silc_strncat(uh, sizeof(uh), hsock->hostname, len);
+           silc_socket_stream_get_info(silc_packet_stream_get_stream(hsock),
+                                       NULL, (const char **)&tmp2, NULL, NULL);
+           silc_strncat(uh, sizeof(uh), tmp2, strlen(tmp2));
          }
 
-         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+         if (idata->conn_type == SILC_CONN_CLIENT)
            channels =
-             silc_server_get_client_channel_list(server, entry, FALSE, 
+             silc_server_get_client_channel_list(server, entry, FALSE,
                                                  FALSE, &umode_list);
          else
            channels =
-             silc_server_get_client_channel_list(server, entry, TRUE, 
+             silc_server_get_client_channel_list(server, entry, TRUE,
                                                  TRUE, &umode_list);
 
          if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
@@ -1200,23 +1665,50 @@ void silc_server_query_send_reply(SilcServer server,
          if (entry->connection)
            SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
 
+         /* If Requested Attribute were present, and we do not have the
+            attributes we will reply to them on behalf of the client. */
+         len = 0;
+         if (query->attrs) {
+           if (!entry->attrs && SILC_IS_LOCAL(entry)) {
+             tmpattrs = silc_server_query_reply_attrs(server, query, entry);
+             entry->attrs = silc_buffer_steal(tmpattrs, &len);
+             entry->attrs_len = len;
+             silc_buffer_free(tmpattrs);
+           }
+           attrs = entry->attrs;
+           len = entry->attrs_len;
+         }
+
          /* Send command reply */
          silc_server_send_command_reply(server, cmd->sock, query->querycmd,
-                                        status, 0, ident, 9,
-                                        2, idp->data, idp->len,
+                                        status, 0, ident, 10,
+                                        2, idp->data, silc_buffer_len(idp),
                                         3, nh, strlen(nh),
                                         4, uh, strlen(uh),
-                                        5, entry->userinfo, 
+                                        5, entry->userinfo,
                                         strlen(entry->userinfo),
                                         6, channels ? channels->data : NULL,
-                                        channels ? channels->len : 0,
+                                        channels ? silc_buffer_len(channels)
+                                        : 0,
                                         7, mode, 4,
                                         8, idle, 4,
                                         9, fingerprint,
                                         fingerprint ? 20 : 0,
                                         10, umode_list ? umode_list->data :
-                                        NULL, umode_list ? umode_list->len :
-                                        0);
+                                        NULL, umode_list ?
+                                        silc_buffer_len(umode_list) :
+                                        0, 11, attrs, len);
+
+         sent_reply = TRUE;
+
+         /* For now we always delete Requested Attributes, unless the client
+            is detached, in which case we don't want to reconstruct the
+            same data everytime */
+         if (!(entry->mode & SILC_UMODE_DETACHED) &&
+             !(entry->data.status & SILC_IDLIST_STATUS_NOATTR)) {
+           silc_free(entry->attrs);
+           entry->attrs = NULL;
+         }
 
          if (channels)
            silc_buffer_free(channels);
@@ -1231,40 +1723,47 @@ void silc_server_query_send_reply(SilcServer server,
        if (!entry->username) {
          silc_server_send_command_reply(server, cmd->sock, query->querycmd,
                                         status, 0, ident, 2,
-                                        2, idp->data, idp->len,
+                                        2, idp->data, silc_buffer_len(idp),
                                         3, nh, strlen(nh));
+         sent_reply = TRUE;
        } else {
+         memset(uh, 0, sizeof(uh));
          silc_strncat(uh, sizeof(uh), entry->username,
                       strlen(entry->username));
          if (!strchr(entry->username, '@') && entry->connection) {
            hsock = entry->connection;
            silc_strncat(uh, sizeof(uh), "@", 1);
-           len = strlen(hsock->hostname);
-           silc_strncat(uh, sizeof(uh), hsock->hostname, len);
+           silc_socket_stream_get_info(silc_packet_stream_get_stream(hsock),
+                                       NULL, (const char **)&tmp2,
+                                       NULL, NULL);
+           silc_strncat(uh, sizeof(uh), tmp2, strlen(tmp2));
          }
 
          silc_server_send_command_reply(server, cmd->sock, query->querycmd,
                                         status, 0, ident, 3,
-                                        2, idp->data, idp->len,
+                                        2, idp->data, silc_buffer_len(idp),
                                         3, nh, strlen(nh),
                                         4, uh, strlen(uh));
+         sent_reply = TRUE;
        }
        break;
 
       case SILC_COMMAND_WHOWAS:
+       memset(uh, 0, sizeof(uh));
        silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
        if (!strchr(entry->username, '@'))
-         silc_strncat(uh, sizeof(uh), "@*private*", 10);
+         silc_strncat(uh, sizeof(uh), "@-private-", 10);
 
        /* Send command reply */
        silc_server_send_command_reply(server, cmd->sock, query->querycmd,
                                       status, 0, ident, 4,
-                                      2, idp->data, idp->len,
+                                      2, idp->data, silc_buffer_len(idp),
                                       3, nh, strlen(nh),
                                       4, uh, strlen(uh),
-                                      5, entry->userinfo, 
-                                      entry->userinfo ? 
+                                      5, entry->userinfo,
+                                      entry->userinfo ?
                                       strlen(entry->userinfo) : 0);
+       sent_reply = TRUE;
        break;
       }
 
@@ -1279,9 +1778,14 @@ void silc_server_query_send_reply(SilcServer server,
       /* Not one valid entry was found, send error.  If nickname was used
         in query send error based on that, otherwise the query->errors
         already includes proper errors. */
-      if (query->nickname)
-       silc_server_query_add_error(server, query, TRUE, 1,
+      if (query->nickname[0] || (!query->ids && query->attrs))
+       silc_server_query_add_error(server, query, 1, 1,
                                    SILC_STATUS_ERR_NO_SUCH_NICK);
+
+      /* Make sure some error is sent */
+      if (!query->errors_count && !servers_count && !channels_count)
+       silc_server_query_add_error(server, query, 2, 0,
+                                   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     }
   }
 
@@ -1295,15 +1799,18 @@ void silc_server_query_send_reply(SilcServer server,
     k = 0;
     for (i = 0; i < servers_count; i++) {
       entry = servers[i];
-      
+
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
+      if (servers_count == 1 && status != SILC_STATUS_OK && !channels_count &&
+         !query->errors_count)
+       status = SILC_STATUS_LIST_END;
       if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
          !query->errors_count)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
        status = SILC_STATUS_LIST_END;
-      
+
       SILC_LOG_DEBUG(("%s: server %s",
                      (status == SILC_STATUS_OK ?         "   OK" :
                       status == SILC_STATUS_LIST_START ? "START" :
@@ -1316,12 +1823,13 @@ void silc_server_query_send_reply(SilcServer server,
       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
                                     status, 0, ident, 2,
-                                    2, idp->data, idp->len, 
-                                    3, entry->server_name, 
-                                    entry->server_name ? 
+                                    2, idp->data, silc_buffer_len(idp),
+                                    3, entry->server_name,
+                                    entry->server_name ?
                                     strlen(entry->server_name) : 0);
       silc_buffer_free(idp);
-      
+      sent_reply = TRUE;
+
       if (status == SILC_STATUS_LIST_END)
        break;
       k++;
@@ -1338,15 +1846,18 @@ void silc_server_query_send_reply(SilcServer server,
     k = 0;
     for (i = 0; i < channels_count; i++) {
       entry = channels[i];
-      
+
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
+      if (channels_count == 1 && status != SILC_STATUS_OK &&
+         !query->errors_count)
+       status = SILC_STATUS_LIST_END;
       if (channels_count > 1 && k == channels_count - 1 &&
          !query->errors_count)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
        status = SILC_STATUS_LIST_END;
-      
+
       SILC_LOG_DEBUG(("%s: channel %s",
                      (status == SILC_STATUS_OK ?         "   OK" :
                       status == SILC_STATUS_LIST_START ? "START" :
@@ -1359,12 +1870,13 @@ void silc_server_query_send_reply(SilcServer server,
       idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
                                     status, 0, ident, 2,
-                                    2, idp->data, idp->len, 
-                                    3, entry->channel_name, 
-                                    entry->channel_name ? 
+                                    2, idp->data, silc_buffer_len(idp),
+                                    3, entry->channel_name,
+                                    entry->channel_name ?
                                     strlen(entry->channel_name) : 0);
       silc_buffer_free(idp);
-      
+      sent_reply = TRUE;
+
       if (status == SILC_STATUS_LIST_END)
        break;
       k++;
@@ -1383,30 +1895,38 @@ void silc_server_query_send_reply(SilcServer server,
       idp = NULL;
 
       /* Take error argument */
-      if (query->errors[i].from_cmd) {
+      if (query->errors[i].type == 1) {
+       /* Take from sent arguments */
+       len = 0;
        tmp = silc_argument_get_arg_type(cmd->args,
                                         query->errors[i].index, &len);
-       if (query->errors[i].index == 1)
-         type = 3;                 /* Nickname */
-       else
-         type = 2;                 /* ID */
+       type = 2;
+      } else if (query->errors[i].type == 2) {
+       /* No argument */
+       len = 0;
+       tmp = NULL;
+       type = 0;
       } else if (!query->errors[i].id) {
+       /* Take from query->ids */
        idp =
          silc_id_payload_encode(query->ids[query->errors[i].index].id,
                                 query->ids[query->errors[k].index].id_type);
        tmp = idp->data;
-       len = idp->len;
+       len = silc_buffer_len(idp);
        type = 2;
       } else {
+       /* Take added ID. */
        idp = silc_id_payload_encode(query->errors[i].id,
                                     query->errors[k].id_type);
        tmp = idp->data;
-       len = idp->len;
+       len = silc_buffer_len(idp);
        type = 2;
       }
 
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
+      if (query->errors_count == 1 && status != SILC_STATUS_OK)
+       status = SILC_STATUS_LIST_END;
       if (query->errors_count > 1 && k == query->errors_count - 1)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
@@ -1417,10 +1937,22 @@ void silc_server_query_send_reply(SilcServer server,
                       status == SILC_STATUS_LIST_START ? "START" :
                       status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
                       status == SILC_STATUS_LIST_END  ?  "  END" :
-                      "      : "), 
+                      "      : "),
                      silc_get_status_message(query->errors[i].error),
                      query->errors[i].error));
 
+#if 1 /* XXX Backwards compatibility.  Remove in 1.0. */
+      if (query->errors[i].error == SILC_STATUS_ERR_NO_SUCH_NICK)
+       /* Send error */
+       silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+                                      (status == SILC_STATUS_OK ?
+                                       query->errors[i].error : status),
+                                      (status == SILC_STATUS_OK ?
+                                       0 : query->errors[i].error), ident, 2,
+                                      type, tmp, len,
+                                      3, tmp, len);
+      else
+#endif
       /* Send error */
       silc_server_send_command_reply(server, cmd->sock, query->querycmd,
                                     (status == SILC_STATUS_OK ?
@@ -1428,7 +1960,9 @@ void silc_server_query_send_reply(SilcServer server,
                                     (status == SILC_STATUS_OK ?
                                      0 : query->errors[i].error), ident, 1,
                                     type, tmp, len);
+
       silc_buffer_free(idp);
+      sent_reply = TRUE;
 
       if (status == SILC_STATUS_LIST_END)
        break;
@@ -1436,10 +1970,166 @@ void silc_server_query_send_reply(SilcServer server,
     }
   }
 
+  if (!sent_reply)
+    SILC_LOG_ERROR(("BUG: Query did not send anything"));
+
   /* Cleanup */
   silc_server_query_free(query);
 }
 
+/* This routine is used to reply to Requested Attributes in WHOIS on behalf
+   of the client since we were unable to resolve them from the client.
+   Either client does not support Requested Attributes or isn't replying
+   to them like it should. */
+
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+                                        SilcServerQuery query,
+                                        SilcClientEntry client_entry)
+{
+  SilcBuffer buffer = NULL;
+  SilcAttribute attribute;
+  SilcAttributePayload attr;
+  SilcAttributeObjPk pk;
+  SilcAttributeObjService service;
+  unsigned char *tmp;
+  unsigned char sign[2048 + 1];
+  SilcUInt32 sign_len;
+
+  SILC_LOG_DEBUG(("Constructing Requested Attributes"));
+
+  /* Go through all requested attributes */
+  silc_dlist_start(query->attrs);
+  while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
+    attribute = silc_attribute_get_attribute(attr);
+    switch (attribute) {
+
+    case SILC_ATTRIBUTE_SERVICE:
+      /* Put SERVICE.  Put only SILC service. */
+      memset(&service, 0, sizeof(service));
+      service.port = (server->config->server_info->primary ?
+                     server->config->server_info->primary->port : SILC_PORT);
+      silc_strncat(service.address, sizeof(service.address),
+                  server->server_name, strlen(server->server_name));
+      service.status = !(client_entry->mode & SILC_UMODE_DETACHED);
+      if (client_entry->connection)
+       service.idle = time(NULL) - client_entry->data.last_receive;
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            &service, sizeof(service));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_STATUS_MOOD:
+      /* Put STATUS_MOOD */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            (void *)
+                                            SILC_ATTRIBUTE_MOOD_NORMAL,
+                                            sizeof(SilcUInt32));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_STATUS_FREETEXT:
+      /* Put STATUS_FREETEXT.  We just tell in the message that we are
+        replying on behalf of the client. */
+      tmp =
+       "This information was provided by the server on behalf of the user";
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            tmp, strlen(tmp));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_PREFERRED_CONTACT:
+      /* Put PREFERRED_CONTACT */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            (void *)
+                                            SILC_ATTRIBUTE_CONTACT_CHAT,
+                                            sizeof(SilcUInt32));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+      /* Put USER_PUBLIC_KEY */
+      if (client_entry->data.public_key) {
+       pk.type = "silc-rsa";
+       pk.data = silc_pkcs_public_key_encode(client_entry->data.public_key,
+                                             &pk.data_len);
+       buffer = silc_attribute_payload_encode(buffer, attribute, pk.data ?
+                                              SILC_ATTRIBUTE_FLAG_VALID :
+                                              SILC_ATTRIBUTE_FLAG_INVALID,
+                                              &pk, sizeof(pk));
+       silc_free(pk.data);
+       if (!buffer)
+         return NULL;
+       break;
+      }
+
+      /* No public key available */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_INVALID,
+                                            NULL, 0);
+      if (!buffer)
+       return NULL;
+      break;
+
+    default:
+      /* Ignore SERVER_PUBLIC_KEY since we are going to put it anyway later */
+      if (attribute == SILC_ATTRIBUTE_SERVER_PUBLIC_KEY ||
+         attribute == SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE)
+       break;
+
+      /* For other attributes we cannot reply so mark it invalid */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_INVALID,
+                                            NULL, 0);
+      if (!buffer)
+       return NULL;
+      break;
+    }
+  }
+
+  /* Always put our public key.  This assures that we send at least
+     something valid back always. */
+  pk.type = "silc-rsa";
+  pk.data = silc_pkcs_public_key_encode(server->public_key, &pk.data_len);
+  buffer = silc_attribute_payload_encode(buffer,
+                                        SILC_ATTRIBUTE_SERVER_PUBLIC_KEY,
+                                        pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
+                                        SILC_ATTRIBUTE_FLAG_INVALID,
+                                        &pk, sizeof(pk));
+  silc_free(pk.data);
+  if (!buffer)
+    return NULL;
+
+  /* Finally compute the digital signature of all the data we provided
+     as an indication that we provided rightfull information, and this
+     also authenticates our public key. */
+  if (silc_pkcs_private_key_get_len(server->private_key) / 8 <=
+      sizeof(sign) -1  &&
+      silc_pkcs_sign(server->private_key, buffer->data,
+                    silc_buffer_len(buffer), sign, sizeof(sign), &sign_len,
+                    TRUE, server->sha1hash)) {
+    pk.type = NULL;
+    pk.data = sign;
+    pk.data_len = sign_len;
+    buffer =
+      silc_attribute_payload_encode(buffer,
+                                   SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE,
+                                   SILC_ATTRIBUTE_FLAG_VALID,
+                                   &pk, sizeof(pk));
+  }
+  if (!buffer)
+    return NULL;
+
+  return buffer;
+}
+
 /* Find client by the Client ID indicated by the `client_id', and if not
    found then query it by using WHOIS command.  The client information
    is also resolved if the cached information is incomplete or if the
@@ -1450,8 +2140,8 @@ void silc_server_query_send_reply(SilcServer server,
 
 SilcClientEntry silc_server_query_client(SilcServer server,
                                         const SilcClientID *client_id,
-                                        bool always_resolve,
-                                        bool *resolved)
+                                        SilcBool always_resolve,
+                                        SilcBool *resolved)
 {
   SilcClientEntry client;
 
@@ -1478,6 +2168,9 @@ SilcClientEntry silc_server_query_client(SilcServer server,
       always_resolve) {
     SilcBuffer buffer, idp;
 
+    /* Statistics */
+    server->stat.commands_sent++;
+
     if (client) {
       client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
@@ -1487,11 +2180,12 @@ SilcClientEntry silc_server_query_client(SilcServer server,
     idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
                                            server->cmd_ident, 1,
-                                           4, idp->data, idp->len);
+                                           4, idp->data,
+                                           silc_buffer_len(idp));
     silc_server_packet_send(server, client ? client->router->connection :
                            SILC_PRIMARY_ROUTE(server),
                            SILC_PACKET_COMMAND, 0,
-                           buffer->data, buffer->len, FALSE);
+                           buffer->data, silc_buffer_len(buffer));
     silc_buffer_free(idp);
     silc_buffer_free(buffer);