Merge commit 'origin/silc.1.1.branch'
[silc.git] / apps / silcd / server_query.c
index 3414c650c517afce54047277d32eab873454dfa7..59eda8024e40cb5d513ef8bc89656311bb9b7150 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2003 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
@@ -22,7 +22,7 @@
 #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 */
@@ -42,25 +42,26 @@ typedef struct {
 typedef struct {
   void *id;                        /* ID */
   SilcIdType id_type;              /* ID type */
-  SilcUInt16 index;                /* Index to IDs */
-  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 */
@@ -69,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,
@@ -89,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,
@@ -120,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);
 
@@ -173,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)
 {
@@ -186,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;
@@ -206,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;
@@ -220,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;
     }
@@ -260,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;
     }
@@ -272,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. */
@@ -286,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);
 
@@ -317,12 +380,39 @@ 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,
@@ -330,53 +420,88 @@ void silc_server_query_send_router_reply(void *context, void *reply)
     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);
+                           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 (tmp_len > 128 ||
-         !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, &id_type);
-       if (!id || id_type != SILC_ID_CLIENT) {
-         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,23 +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);
              query->ids = NULL;
              query->ids_count = 0;
-             silc_free(id);
-             return;
+             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++;
       }
@@ -425,20 +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 && 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 (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-         cmd->sock->user_data)
-       ((SilcClientEntry)cmd->sock->user_data)->fast_command = 6;
-    }
-    break;
+   break;
 
   case SILC_COMMAND_WHOWAS:
     /* Get nickname */
@@ -447,17 +559,31 @@ 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 (tmp_len > 128 ||
-       !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
+       !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);
@@ -475,26 +601,62 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
       if (tmp) {
        /* Get the nickname@server string and parse it */
        if (tmp_len > 128 ||
-           !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
-         silc_server_query_add_error(server, query, TRUE, 1,
+           !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 && tmp_len <= 256)
-       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 && !query->server_name && !query->channel_name) {
+      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;
+       return FALSE;
       }
 
     } else {
@@ -506,9 +668,8 @@ 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,
+       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;
        }
@@ -516,20 +677,20 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        /* 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 (id_type == SILC_ID_CLIENT) {
+         if (id.type == SILC_ID_CLIENT) {
            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);
                query->ids = NULL;
                query->ids_count = 0;
-               silc_free(id);
-               return;
+               return FALSE;
              }
            }
          } else {
@@ -541,13 +702,20 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
            silc_free(query->ids);
            query->ids = NULL;
            query->ids_count = 0;
-           silc_free(id);
-           return;
+           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++;
       }
     }
@@ -559,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
@@ -568,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;
@@ -584,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,
+                                        query->nickname,
+                                        query->nick_server[0] ?
+                                        query->nick_server : NULL,
+                                        server->md5hash,
                                         &clients, &clients_count))
       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,
+                                          query->nickname,
+                                          query->nick_server[0] ?
+                                          query->nick_server : NULL,
+                                          server->md5hash,
                                           &clients, &clients_count))
        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);
   }
 
@@ -628,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);
   }
 
@@ -646,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);
   }
 
@@ -667,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;
        }
@@ -685,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;
        }
@@ -702,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;
        }
@@ -718,6 +1084,11 @@ 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));
@@ -788,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. */
@@ -867,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;
@@ -911,12 +1287,15 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
        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 */
@@ -987,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);
@@ -1099,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
@@ -1126,14 +1512,16 @@ 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];
-  bool sent_reply = FALSE;
+  char nh[384], uh[384];
+  SilcBool sent_reply = FALSE;
 
   SILC_LOG_DEBUG(("Sending reply to query"));
   SILC_LOG_DEBUG(("Sending %d clients", clients_count));
@@ -1146,11 +1534,14 @@ void silc_server_query_send_reply(SilcServer server,
   /* 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 ||
@@ -1217,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));
@@ -1245,16 +1635,19 @@ void silc_server_query_send_reply(SilcServer server,
 
          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,
                                                  FALSE, &umode_list);
@@ -1278,8 +1671,8 @@ void silc_server_query_send_reply(SilcServer server,
          if (query->attrs) {
            if (!entry->attrs && SILC_IS_LOCAL(entry)) {
              tmpattrs = silc_server_query_reply_attrs(server, query, entry);
-             entry->attrs = silc_memdup(tmpattrs->data, tmpattrs->len);
-             entry->attrs_len = tmpattrs->len;
+             entry->attrs = silc_buffer_steal(tmpattrs, &len);
+             entry->attrs_len = len;
              silc_buffer_free(tmpattrs);
            }
            attrs = entry->attrs;
@@ -1289,19 +1682,21 @@ void silc_server_query_send_reply(SilcServer server,
          /* Send command reply */
          silc_server_send_command_reply(server, cmd->sock, query->querycmd,
                                         status, 0, ident, 10,
-                                        2, idp->data, idp->len,
+                                        2, idp->data, silc_buffer_len(idp),
                                         3, nh, strlen(nh),
                                         4, uh, strlen(uh),
                                         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 :
+                                        NULL, umode_list ?
+                                        silc_buffer_len(umode_list) :
                                         0, 11, attrs, len);
 
          sent_reply = TRUE;
@@ -1328,22 +1723,25 @@ 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;
@@ -1351,14 +1749,15 @@ void silc_server_query_send_reply(SilcServer server,
        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,
@@ -1379,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);
     }
   }
 
@@ -1419,7 +1823,7 @@ 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,
+                                    2, idp->data, silc_buffer_len(idp),
                                     3, entry->server_name,
                                     entry->server_name ?
                                     strlen(entry->server_name) : 0);
@@ -1466,7 +1870,7 @@ 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,
+                                    2, idp->data, silc_buffer_len(idp),
                                     3, entry->channel_name,
                                     entry->channel_name ?
                                     strlen(entry->channel_name) : 0);
@@ -1491,26 +1895,31 @@ 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;
       }
 
@@ -1532,6 +1941,18 @@ void silc_server_query_send_reply(SilcServer server,
                      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 ?
@@ -1539,6 +1960,7 @@ 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;
 
@@ -1688,10 +2110,11 @@ SilcBuffer silc_server_query_reply_attrs(SilcServer server,
   /* 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_get_key_len(server->pkcs) / 8 <= sizeof(sign) -1  &&
-      silc_pkcs_sign_with_hash(server->pkcs, server->sha1hash,
-                              buffer->data, buffer->len,
-                              sign, &sign_len)) {
+  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;
@@ -1717,8 +2140,8 @@ SilcBuffer silc_server_query_reply_attrs(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;
 
@@ -1745,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;
@@ -1754,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);