Watcher list support added.
[silc.git] / apps / silcd / server.c
index ffc7f0fc5c2cc67e12d8afdfcc7cc8d3b5bb0b9f..09c54b7d9af231dd659546308b98d75e04fafdaa 100644 (file)
@@ -44,6 +44,7 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote);
 SILC_TASK_CALLBACK(silc_server_channel_key_rekey);
 SILC_TASK_CALLBACK(silc_server_failure_callback);
 SILC_TASK_CALLBACK(silc_server_rekey_callback);
+SILC_TASK_CALLBACK(silc_server_get_stats);
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -87,10 +88,15 @@ void silc_server_free(SilcServer server)
     silc_dlist_uninit(server->sim);
 #endif
 
+    silc_server_config_unref(&server->config_ref);
     if (server->rng)
       silc_rng_free(server->rng);
     if (server->pkcs)
       silc_pkcs_free(server->pkcs);
+    if (server->public_key)
+      silc_pkcs_public_key_free(server->public_key);
+    if (server->private_key)
+      silc_pkcs_private_key_free(server->private_key);
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
     if (server->id_entry)
@@ -102,12 +108,31 @@ void silc_server_free(SilcServer server)
     silc_idcache_free(server->global_list->clients);
     silc_idcache_free(server->global_list->servers);
     silc_idcache_free(server->global_list->channels);
+    silc_hash_table_free(server->watcher_list);
 
     silc_free(server->sockets);
     silc_free(server);
   }
 }
 
+/* Opens a listening port.
+   XXX This function will become more general and will support multiple
+   listening ports */
+
+static bool silc_server_listen(SilcServer server, int *sock)
+{
+
+  *sock = silc_net_create_server(server->config->server_info->port,
+                               server->config->server_info->server_ip);
+  if (*sock < 0) {
+    SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
+                   server->config->server_info->server_ip,
+                   server->config->server_info->port));
+    return FALSE;
+  }
+  return TRUE;
+}
+
 /* Initializes the entire SILC server. This is called always before running
    the server. This is called only once at the initialization of the program.
    This binds the server to its listenning port. After this function returns
@@ -115,29 +140,27 @@ void silc_server_free(SilcServer server)
    when everything is ok to run the server. Configuration file must be
    read and parsed before calling this. */
 
-int silc_server_init(SilcServer server)
+bool silc_server_init(SilcServer server)
 {
   int sock;
   SilcServerID *id;
   SilcServerEntry id_entry;
   SilcIDListPurge purge;
+  SilcSocketConnection newsocket = NULL;
 
   SILC_LOG_DEBUG(("Initializing server"));
-  assert(server);
-  assert(server->config);
-
-  /* Set public and private keys */
-  if (!server->config->server_info ||
-      !server->config->server_info->public_key ||
-      !server->config->server_info->private_key) {
-    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
-    return FALSE;
-  }
+
+  server->starttime = time(NULL);
+
+  /* Take config object for us */
+  silc_server_config_ref(&server->config_ref, server->config, 
+                        server->config);
+
+  /* Steal public and private key from the config object */
   server->public_key = server->config->server_info->public_key;
   server->private_key = server->config->server_info->private_key;
-
-  /* Set default to configuration parameters */
-  silc_server_config_set_defaults(server);
+  server->config->server_info->public_key = NULL;
+  server->config->server_info->private_key = NULL;
 
   /* Register all configured ciphers, PKCS and hash functions. */
   if (!silc_server_config_register_ciphers(server))
@@ -159,19 +182,18 @@ int silc_server_init(SilcServer server)
   silc_hash_alloc("sha1", &server->sha1hash);
 
   /* Allocate PKCS context for local public and private keys */
-  silc_pkcs_alloc(server->public_key->name, &server->pkcs);
+  if (!silc_pkcs_alloc(server->public_key->name, &server->pkcs))
+    goto err;
   silc_pkcs_public_key_set(server->pkcs, server->public_key);
   silc_pkcs_private_key_set(server->pkcs, server->private_key);
 
-  /* Create a listening server */
-  sock = silc_net_create_server(server->config->server_info->port,
-                               server->config->server_info->server_ip);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
-                   server->config->server_info->server_ip,
-                   server->config->server_info->port));
+  /* Initialize the scheduler */
+  server->schedule = silc_schedule_init(server->config->param.connections_max);
+  if (!server->schedule)
     goto err;
-  }
+
+  /* First, register log files configuration for error output */
+  silc_server_config_setlogfiles(server);
 
   /* Initialize ID caches */
   server->local_list->clients =
@@ -187,86 +209,92 @@ int silc_server_init(SilcServer server)
   server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
   server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 
+  /* Init watcher list */
+  server->watcher_list = 
+    silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
+                         silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
+                         NULL, NULL, TRUE);
+  if (!server->watcher_list)
+    goto err;
+
+  /* Create a listening server */
+  if (!silc_server_listen(server, &sock))
+    goto err;
+
+  /* Set socket to non-blocking mode */
+  silc_net_set_socket_nonblock(sock);
+  server->sock = sock;
+
   /* Allocate the entire socket list that is used in server. Eventually
      all connections will have entry in this table (it is a table of
      pointers to the actual object that is allocated individually
      later). */
   server->sockets = silc_calloc(server->config->param.connections_max,
                                sizeof(*server->sockets));
+  if (!server->sockets)
+    goto err;
 
-  do {
-    SilcSocketConnection newsocket = NULL;
-
-    /* Set socket to non-blocking mode */
-    silc_net_set_socket_nonblock(sock);
-    server->sock = sock;
-
-    /* Add ourselves also to the socket table. The entry allocated above
-       is sent as argument for fast referencing in the future. */
-    silc_socket_alloc(sock, SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
-    server->sockets[sock] = newsocket;
-
-    /* Perform name and address lookups to resolve the listenning address
-       and port. */
-    if (!silc_net_check_local_by_sock(sock, &newsocket->hostname,
-                                     &newsocket->ip)) {
-      if ((server->config->require_reverse_lookup && !newsocket->hostname) ||
-         !newsocket->ip) {
-       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
-                       newsocket->hostname ? newsocket->hostname :
-                       newsocket->ip ? newsocket->ip : ""));
-       server->stat.conn_failures++;
-       goto err;
-      }
-      if (!newsocket->hostname)
-       newsocket->hostname = strdup(newsocket->ip);
-    }
-    newsocket->port = silc_net_get_local_port(sock);
-
-    /* Create a Server ID for the server. */
-    silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
-    if (!id)
-      goto err;
+  /* Add ourselves also to the socket table. The entry allocated above
+     is sent as argument for fast referencing in the future. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
 
-    server->id = id;
-    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
-    server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
-    server->id_type = SILC_ID_SERVER;
-    server->server_name = server->config->server_info->server_name;
-
-    /* Add ourselves to the server list. We don't have a router yet
-       beacuse we haven't established a route yet. It will be done later.
-       For now, NULL is sent as router. This allocates new entry to
-       the ID list. */
-    id_entry =
-      silc_idlist_add_server(server->local_list, strdup(server->server_name),
-                            server->server_type, server->id, NULL, NULL);
-    if (!id_entry) {
-      SILC_LOG_ERROR(("Could not add ourselves to cache"));
+  /* Perform name and address lookups to resolve the listenning address
+     and port. */
+  if (!silc_net_check_local_by_sock(sock, &newsocket->hostname,
+                                   &newsocket->ip)) {
+    if ((server->config->require_reverse_lookup && !newsocket->hostname) ||
+       !newsocket->ip) {
+      SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+                     newsocket->hostname ? newsocket->hostname :
+                     newsocket->ip ? newsocket->ip : ""));
+      server->stat.conn_failures++;
       goto err;
     }
-    id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+    if (!newsocket->hostname)
+      newsocket->hostname = strdup(newsocket->ip);
+  }
+  newsocket->port = silc_net_get_local_port(sock);
 
-    /* Put the allocated socket pointer also to the entry allocated above
-       for fast back-referencing to the socket list. */
-    newsocket->user_data = (void *)id_entry;
-    id_entry->connection = (void *)newsocket;
-    server->id_entry = id_entry;
-  } while (0);
+  /* Create a Server ID for the server. */
+  silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
+  if (!id)
+    goto err;
+
+  server->id = id;
+  server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
+  server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
+  server->id_type = SILC_ID_SERVER;
+  server->server_name = server->config->server_info->server_name;
+  server->config->server_info->server_name = NULL;
+
+  /* Add ourselves to the server list. We don't have a router yet
+     beacuse we haven't established a route yet. It will be done later.
+     For now, NULL is sent as router. This allocates new entry to
+     the ID list. */
+  id_entry =
+    silc_idlist_add_server(server->local_list, strdup(server->server_name),
+                          server->server_type, server->id, NULL, NULL);
+  if (!id_entry) {
+    SILC_LOG_ERROR(("Could not add ourselves to cache"));
+    goto err;
+  }
+  id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+  /* Put the allocated socket pointer also to the entry allocated above
+     for fast back-referencing to the socket list. */
+  newsocket->user_data = (void *)id_entry;
+  id_entry->connection = (void *)newsocket;
+  server->id_entry = id_entry;
 
   /* Register protocols */
   silc_server_protocols_register();
 
-  /* Initialize the scheduler. */
-  server->schedule = silc_schedule_init(server->config->param.connections_max);
-  if (!server->schedule)
-    goto err;
-
   /* Add the first task to the scheduler. This is task that is executed by
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  silc_schedule_task_add(server->schedule, sock,
+  silc_schedule_task_add(server->schedule, 0,
                         silc_server_connect_to_router,
                         (void *)server, 0, 1,
                         SILC_TASK_TIMEOUT,
@@ -282,9 +310,6 @@ int silc_server_init(SilcServer server)
                         SILC_TASK_PRI_NORMAL);
   server->listenning = TRUE;
 
-  /* Send log file configuration */
-  silc_server_config_setlogfiles(server);
-
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
   if (server->config->servers) {
@@ -325,117 +350,128 @@ int silc_server_init(SilcServer server)
                         (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
+  /* If we are normal server we'll retrieve network statisticial information
+     once in a while from the router. */
+  if (server->server_type == SILC_SERVER)
+    silc_schedule_task_add(purge->schedule, 0, silc_server_get_stats,
+                          server, 10, 0, SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
+
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
   return TRUE;
 
  err:
+  silc_server_config_unref(&server->config_ref);
   silc_net_close_server(sock);
   return FALSE;
 }
 
-/* Fork server to background */
+/* This function basically reads the config file again and switches the config
+   object pointed by the server object. After that, we have to fix various
+   things such as the server_name and the listening ports.
+   Keep in mind that we no longer have the root privileges at this point. */
 
-void silc_server_daemonise(SilcServer server)
+bool silc_server_rehash(SilcServer server)
 {
-  int i;
+  SilcServerConfig newconfig;
 
-  SILC_LOG_DEBUG(("Forking SILC server to background"));
+  SILC_LOG_INFO(("Rehashing server"));
 
-  i = fork();
+  /* Reset the logging system */
+  silc_log_quick = TRUE;
+  silc_log_flush_all();
 
-  if (i < 0) {
-    SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
-    exit(1);
-  }
-  else if (i) {
-    if (geteuid())
-      SILC_LOG_DEBUG(("Server started as user"));
-    else
-      SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
-    exit(0);
+  /* Start the main rehash phase (read again the config file) */
+  newconfig = silc_server_config_alloc(server->config_file);
+  if (!newconfig) {
+    SILC_LOG_ERROR(("Rehash FAILED."));
+    return FALSE;
   }
-  setsid();
-}
 
-/* Drop root privligies. If this cannot be done, die. */
+  /* Reinit scheduler if necessary */
+  if (newconfig->param.connections_max > server->config->param.connections_max)
+    if (!silc_schedule_reinit(server->schedule, 
+                             newconfig->param.connections_max))
+      return FALSE;
 
-void silc_server_drop(SilcServer server)
-{
-  /* Are we executing silcd as root or a regular user? */
-  if (!geteuid()) {
-    struct passwd *pw;
-    struct group *gr;
-    char *user, *group;
-
-    /* Get the values given for user and group in configuration file */
-    user = server->config->server_info->user;
-    group = server->config->server_info->group;
-
-    if (!user || !group) {
-      fprintf(stderr, "Error:" /* XXX update this error message */
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the [Identity] configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    }
+  /* Fix the server_name field */
+  if (strcmp(server->server_name, newconfig->server_info->server_name)) {
+    silc_free(server->server_name);
+    server->server_name = newconfig->server_info->server_name;
+    newconfig->server_info->server_name = NULL;
+
+    /* Update the idcache list with a fresh pointer */
+    silc_free(server->id_entry->server_name);
+    server->id_entry->server_name = strdup(server->server_name);
+    if (!silc_idcache_del_by_context(server->local_list->servers, 
+                                    server->id_entry))
+      return FALSE;
+    if (!silc_idcache_add(server->local_list->servers,
+                         server->id_entry->server_name,
+                         server->id_entry->id, server->id_entry, 0, NULL))
+      return FALSE;
+  }
 
-    /* Check whether the user/group does not begin with a number */
-    if (isdigit(user[0]) || isdigit(group[0])) {
-      SILC_LOG_DEBUG(("User and/or group starts with a number"));
-      fprintf(stderr, "Invalid user and/or group information\n");
-      fprintf(stderr, "Please assign them as names, not numbers\n");
-      exit(1);
-    }
+  /* Set logging */
+  silc_server_config_setlogfiles(server);
 
-    if (!(pw = getpwnam(user))) {
-      fprintf(stderr, "Error: No such user %s found.\n", user);
-      exit(1);
-    }
-    if (!(gr = getgrnam(group))) {
-      fprintf(stderr, "Error: No such group %s found.\n", group);
-      exit(1);
-    }
+  /* Change new key pair if necessary */
+  if (newconfig->server_info->public_key &&
+      !silc_pkcs_public_key_compare(server->public_key,
+                                   newconfig->server_info->public_key)) {
+    silc_pkcs_public_key_free(server->public_key);
+    silc_pkcs_private_key_free(server->private_key);
+    server->public_key = newconfig->server_info->public_key;
+    server->private_key = newconfig->server_info->private_key;
+    newconfig->server_info->public_key = NULL;
+    newconfig->server_info->private_key = NULL;
+
+    /* Allocate PKCS context for local public and private keys */
+    silc_pkcs_free(server->pkcs);
+    if (!silc_pkcs_alloc(server->public_key->name, &server->pkcs))
+      return FALSE;
+    silc_pkcs_public_key_set(server->pkcs, server->public_key);
+    silc_pkcs_private_key_set(server->pkcs, server->private_key);
+  }
 
-    /* Check whether user and/or group is set to root. If yes, exit
-       immediately. Otherwise, setgid and setuid server to user.group */
-    if ((gr->gr_gid == 0) || (pw->pw_uid == 0)) {
-      fprintf(stderr, "Error:"
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the [Identity] configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    }
+  /* Go through all configured routers after rehash */
+  silc_schedule_task_add(server->schedule, 0,
+                        silc_server_connect_to_router,
+                        (void *)server, 0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 
-    SILC_LOG_DEBUG(("Changing to group %s (gid=%u)", group, gr->gr_gid));
-    if (setgid(gr->gr_gid) != 0) {
-      fprintf(stderr, "Error: Failed setgid() to %s (gid=%u). Exiting.\n",
-             group, gr->gr_gid);
-      exit(1);
-    }
-#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
-    SILC_LOG_DEBUG(("Removing supplementary groups"));
-    if (setgroups(0, NULL) != 0) {
-      fprintf(stderr, "Error: Failed setgroups() to NULL. Exiting.\n");
-      exit(1);
-    }
-    SILC_LOG_DEBUG(("Setting supplementary groups for user %s", user));
-    if (initgroups(user, gr->gr_gid) != 0) {
-      fprintf(stderr, "Error: Failed initgroups() for user %s (gid=%u). "
-             "Exiting.\n", user, gr->gr_gid);
-      exit(1);
-    }
-#endif
-    SILC_LOG_DEBUG(("Changing to user %s (uid=%u)", user, pw->pw_uid));
-    if (setuid(pw->pw_uid) != 0) {
-      fprintf(stderr, "Error: Failed to setuid() to %s (gid=%u). Exiting.\n",
-              user, pw->pw_uid);
-      exit(1);
+  /* Check whether our router status has changed */
+  if (newconfig->servers) {
+    SilcServerConfigServer *ptr = newconfig->servers;
+
+    server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       server->id_entry->server_type = SILC_BACKUP_ROUTER;
+       break;
+      }
+      ptr = ptr->next;
     }
   }
+
+  /* Our old config is gone now. We'll unreference our reference made in
+     silc_server_init and then destroy it since we are destroying it
+     underneath the application (layer which called silc_server_init). */
+  silc_server_config_unref(&server->config_ref);
+  silc_server_config_destroy(server->config);
+
+  /* Take new config context */
+  server->config = newconfig;
+  silc_server_config_ref(&server->config_ref, server->config, server->config);
+
+  SILC_LOG_DEBUG(("Server rehashed"));
+
+  return TRUE;
 }
 
 /* The heart of the server. This runs the scheduler thus runs the server.
@@ -444,8 +480,6 @@ void silc_server_drop(SilcServer server)
 
 void silc_server_run(SilcServer server)
 {
-  SILC_LOG_DEBUG(("Running server"));
-
   SILC_LOG_INFO(("SILC Server started"));
 
   /* Start the scheduler, the heart of the SILC server. When this returns
@@ -483,7 +517,8 @@ void silc_server_start_key_exchange(SilcServer server,
   SilcSocketConnection newsocket;
   SilcProtocol protocol;
   SilcServerKEInternalContext *proto_ctx;
-  SilcServerConfigRouter *conn = sconn->conn;
+  SilcServerConfigRouter *conn =
+    (SilcServerConfigRouter *) sconn->conn.ref_ptr;
   void *context;
 
   /* Cancel any possible retry timeouts */
@@ -558,8 +593,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
 {
   SilcServerConnection sconn = (SilcServerConnection)context;
   SilcServer server = sconn->server;
+  SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
   SilcServerConfigConnParams *param =
-    (sconn->param ? sconn->param : &server->config->param);
+               (conn->param ? conn->param : &server->config->param);
 
   SILC_LOG_INFO(("Retrying connecting to a router"));
 
@@ -576,16 +612,21 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
     silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
 
   /* If we've reached max retry count, give up. */
-  if (sconn->retry_count > param->reconnect_count &&
-      param->reconnect_keep_trying == FALSE) {
+  if ((sconn->retry_count > param->reconnect_count) &&
+      !param->reconnect_keep_trying) {
     SILC_LOG_ERROR(("Could not connect to router, giving up"));
+    silc_server_config_unref(&sconn->conn);
     silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
     silc_free(sconn);
     return;
   }
 
+  /* We will lookup a fresh pointer later */
+  silc_server_config_unref(&sconn->conn);
+
   /* Wait one before retrying */
-  silc_schedule_task_add(server->schedule, fd, silc_server_connect_router,
+  silc_schedule_task_add(server->schedule, 0, silc_server_connect_router,
                         context, sconn->retry_timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
@@ -596,6 +637,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
 {
   SilcServerConnection sconn = (SilcServerConnection)context;
   SilcServer server = sconn->server;
+  SilcServerConfigRouter *rconn;
   int sock;
 
   SILC_LOG_INFO(("Connecting to the %s %s on port %d",
@@ -603,6 +645,18 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
                 sconn->remote_host, sconn->remote_port));
 
   server->router_connect = time(NULL);
+  rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
+                                             sconn->remote_port);
+  if (!rconn) {
+    SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
+                  (sconn->backup ? "backup router" : "router"),
+                  sconn->remote_host, sconn->remote_port));
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+  silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
 
   /* Connect to remote host */
   sock = silc_net_create_connection(server->config->server_info->server_ip,
@@ -612,10 +666,12 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
     SILC_LOG_ERROR(("Could not connect to router %s:%d",
                    sconn->remote_host, sconn->remote_port));
     if (!sconn->no_reconnect)
-      silc_schedule_task_add(server->schedule, fd,
+      silc_schedule_task_add(server->schedule, 0,
                             silc_server_connect_to_router_retry,
                             context, 0, 1, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
+    else
+      silc_server_config_unref(&sconn->conn);
     return;
   }
 
@@ -644,9 +700,22 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
     SILC_LOG_DEBUG(("We are backup router/normal server"));
   }
 
+  if (!server->config->routers) {
+    /* There wasn't a configured router, we will continue but we don't
+       have a connection to outside world.  We will be standalone server. */
+    SILC_LOG_DEBUG(("No router(s), server will be standalone"));
+    server->standalone = TRUE;
+    return;
+  }
+
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router_retry);
+
   /* Create the connections to all our routes */
-  ptr = server->config->routers;
-  while (ptr) {
+  for (ptr = server->config->routers; ptr; ptr = ptr->next) {
 
     SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
                    ptr->backup_router ? "Backup router" : "Router",
@@ -654,6 +723,17 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
                    ptr->host, ptr->port));
 
     if (ptr->initiator) {
+      /* Check whether we are connected to this host already */
+      if (silc_server_num_sockets_by_remote(server, 
+                                           silc_net_is_ip(ptr->host) ?
+                                           ptr->host : NULL,
+                                           silc_net_is_ip(ptr->host) ?
+                                           NULL : ptr->host, ptr->port,
+                                           SILC_SOCKET_TYPE_ROUTER)) {
+       SILC_LOG_DEBUG(("We are already connected to this router"));
+       continue;
+      }
+
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
@@ -668,26 +748,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       if (!server->router_conn && !sconn->backup)
        server->router_conn = sconn;
 
-      sconn->conn = ptr;
-      sconn->param = ptr->param;
-
-      silc_schedule_task_add(server->schedule, fd,
+      silc_schedule_task_add(server->schedule, 0,
                             silc_server_connect_router,
                             (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
     }
-
-    if (!ptr->next)
-      return;
-
-    ptr = ptr->next;
   }
-
-  SILC_LOG_DEBUG(("No router(s), server will be standalone"));
-
-  /* There wasn't a configured router, we will continue but we don't
-     have a connection to outside world.  We will be standalone server. */
-  server->standalone = TRUE;
 }
 
 /* Second part of connecting to router(s). Key exchange protocol has been
@@ -718,6 +784,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
@@ -745,6 +815,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
@@ -765,11 +839,11 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
 
   /* Resolve the authentication method used in this connection. Check if
      we find a match from user configured connections */
-  if (!sconn->conn)
+  if (!sconn->conn.ref_ptr)
     conn = silc_server_config_find_router_conn(server, sock->hostname,
                                               sock->port);
   else
-    conn = sconn->conn;
+    conn = sconn->conn.ref_ptr;
 
   if (conn) {
     /* Match found. Use the configured authentication method. Take only
@@ -799,6 +873,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
@@ -852,7 +930,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   unsigned char *id_string;
   SilcUInt32 id_len;
   SilcIDListData idata;
-  SilcServerConfigConnParams *param;
+  SilcServerConfigRouter *conn = NULL;
+  SilcServerConfigConnParams *param = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -932,7 +1011,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   idata = (SilcIDListData)sock->user_data;
   idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 
-  param = (sconn->param ? sconn->param : &server->config->param);
+  conn = sconn->conn.ref_ptr;
+  param = &server->config->param;
+  if (conn && conn->param)
+    param = conn->param;
 
   /* Perform keepalive. The `hb_context' will be freed automatically
      when finally calling the silc_socket_free function. */
@@ -982,6 +1064,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
  out:
   /* Free the temporary connection data context */
   if (sconn) {
+    silc_server_config_unref(&sconn->conn);
     silc_free(sconn->remote_host);
     silc_free(sconn->backup_replace_ip);
     silc_free(sconn);
@@ -1002,7 +1085,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   silc_free(ctx);
 }
 
-/* Host lookup callbcak that is called after the incoming connection's
+/* Host lookup callback that is called after the incoming connection's
    IP and FQDN lookup is performed. This will actually check the acceptance
    of the incoming connection and will register the key exchange protocol
    for this connection. */
@@ -1053,7 +1136,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   if (deny) {
     /* The connection is denied */
     SILC_LOG_INFO(("Connection %s (%s) is denied",
-                   sock->hostname, sock->ip));
+                  sock->hostname, sock->ip));
     silc_server_disconnect_remote(server, sock, deny->reason ?
                                  deny->reason :
                                  "Server closed connection: "
@@ -1062,7 +1145,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
     return;
   }
 
-  /* Check whether we have configred this sort of connection at all. We
+  /* Check whether we have configured this sort of connection at all. We
      have to check all configurations since we don't know what type of
      connection this is. */
   if (!(cconfig = silc_server_config_find_client(server, sock->ip)))
@@ -1094,12 +1177,12 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   proto_ctx->sock = sock;
   proto_ctx->rng = server->rng;
   proto_ctx->responder = TRUE;
-  proto_ctx->cconfig = cconfig;
-  proto_ctx->sconfig = sconfig;
-  proto_ctx->rconfig = rconfig;
+  silc_server_config_ref(&proto_ctx->cconfig, server->config, cconfig);
+  silc_server_config_ref(&proto_ctx->sconfig, server->config, sconfig);
+  silc_server_config_ref(&proto_ctx->rconfig, server->config, rconfig);
 
   /* Take flags for key exchange. Since we do not know what type of connection
-     this is, we go through all found configuratios and use the global ones
+     this is, we go through all found configurations and use the global ones
      as well. This will result always into strictest key exchange flags. */
   SILC_GET_SKE_FLAGS(cconfig, proto_ctx);
   SILC_GET_SKE_FLAGS(sconfig, proto_ctx);
@@ -1149,8 +1232,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   /* Check for maximum allowed connections */
   if (sock > server->config->param.connections_max) {
-    SILC_LOG_ERROR(("Refusing connection, server is full, try again later"));
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
     server->stat.conn_failures++;
+    silc_net_close_connection(sock);
     return;
   }
 
@@ -1188,8 +1272,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+  if ((protocol->state == SILC_PROTOCOL_STATE_ERROR) ||
+      (protocol->state == SILC_PROTOCOL_STATE_FAILURE)) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     sock->protocol = NULL;
@@ -1199,6 +1283,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
+    silc_server_config_unref(&ctx->cconfig);
+    silc_server_config_unref(&ctx->sconfig);
+    silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
@@ -1227,6 +1314,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
+    silc_server_config_unref(&ctx->cconfig);
+    silc_server_config_unref(&ctx->sconfig);
+    silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
@@ -1304,6 +1394,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
+    silc_server_config_unref(&ctx->cconfig);
+    silc_server_config_unref(&ctx->sconfig);
+    silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
@@ -1319,7 +1412,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry client;
-      SilcServerConfigClient *conn = ctx->cconfig;
+      SilcServerConfigClient *conn = ctx->cconfig.ref_ptr;
 
       /* Verify whether this connection is after all allowed to connect */
       if (!silc_server_connection_allowed(server, sock, ctx->conn_type,
@@ -1351,8 +1444,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       /* Statistics */
       server->stat.my_clients++;
       server->stat.clients++;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.cell_clients++;
+      server->stat.cell_clients++;
 
       /* Get connection parameters */
       if (conn->param) {
@@ -1372,8 +1464,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       bool backup_router = FALSE;
       char *backup_replace_ip = NULL;
       SilcUInt16 backup_replace_port = 0;
-      SilcServerConfigServer *sconn = ctx->sconfig;
-      SilcServerConfigRouter *rconn = ctx->rconfig;
+      SilcServerConfigServer *sconn = ctx->sconfig.ref_ptr;
+      SilcServerConfigRouter *rconn = ctx->rconfig.ref_ptr;
 
       if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
        /* Verify whether this connection is after all allowed to connect */
@@ -1526,6 +1618,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   if (ctx->ske)
     silc_ske_free(ctx->ske);
   silc_free(ctx->dest_id);
+  silc_server_config_unref(&ctx->cconfig);
+  silc_server_config_unref(&ctx->sconfig);
+  silc_server_config_unref(&ctx->rconfig);
   silc_free(ctx);
   sock->protocol = NULL;
 }
@@ -1994,7 +2089,6 @@ void silc_server_packet_parse_type(SilcServer server,
 
     if (sock->protocol && sock->protocol->protocol &&
        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
-
       SilcServerKEInternalContext *proto_ctx =
        (SilcServerKEInternalContext *)sock->protocol->context;
 
@@ -2250,6 +2344,14 @@ void silc_server_packet_parse_type(SilcServer server,
     silc_server_ftp(server, sock, packet);
     break;
 
+  case SILC_PACKET_RESUME_CLIENT:
+    /* Resume client */
+    SILC_LOG_DEBUG(("Resume Client packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_resume_client(server, sock, packet);
+    break;
+
   case SILC_PACKET_RESUME_ROUTER:
     /* Resume router packet received. This packet is received for backup
        router resuming protocol. */
@@ -2279,7 +2381,6 @@ void silc_server_create_connection(SilcServer server,
   sconn->remote_host = strdup(remote_host);
   sconn->remote_port = port;
   sconn->no_reconnect = TRUE;
-  sconn->param = &server->config->param;
 
   silc_schedule_task_add(server->schedule, 0,
                         silc_server_connect_router,
@@ -2414,13 +2515,22 @@ void silc_server_free_client_data(SilcServer server,
     silc_server_remove_from_channels(server, NULL, client,
                                     FALSE, NULL, FALSE);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_SIGNOFF);
+
+  /* Remove this client from watcher list if it is */
+  silc_server_del_from_watcher_list(server, client);
+
   /* Update statistics */
   server->stat.my_clients--;
   server->stat.clients--;
-  if (server->server_type == SILC_ROUTER)
+  if (server->stat.cell_clients)
     server->stat.cell_clients--;
   SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
   SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+  silc_schedule_task_del_by_context(server->schedule, client);
 
   /* We will not delete the client entry right away. We will take it
      into history (for WHOWAS command) for 5 minutes */
@@ -2431,9 +2541,9 @@ void silc_server_free_client_data(SilcServer server,
                         (void *)i, 300, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
   client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+  client->mode = 0;
   client->router = NULL;
   client->connection = NULL;
-  client->mode = 0;
 }
 
 /* Frees user_data pointer from socket connection object. This also sends
@@ -2923,6 +3033,9 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 
   server->stat.my_channels++;
 
+  if (server->server_type == SILC_ROUTER)
+    entry->users_resolved = TRUE;
+
   return entry;
 }
 
@@ -2987,6 +3100,9 @@ silc_server_create_new_channel_with_id(SilcServer server,
 
   server->stat.my_channels++;
 
+  if (server->server_type == SILC_ROUTER)
+    entry->users_resolved = TRUE;
+
   return entry;
 }
 
@@ -3796,6 +3912,7 @@ void silc_server_save_users_on_channel(SilcServer server,
   SilcClientID *client_id;
   SilcClientEntry client;
   SilcIDCacheEntry cache;
+  SilcChannelClientEntry chl;
   bool global;
 
   SILC_LOG_DEBUG(("Start"));
@@ -3859,35 +3976,150 @@ void silc_server_save_users_on_channel(SilcServer server,
 
     silc_free(client_id);
 
-    if (!silc_server_client_on_channel(client, channel, NULL)) {
+    if (!silc_server_client_on_channel(client, channel, &chl)) {
       /* Client was not on the channel, add it. */
-      SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+      chl = silc_calloc(1, sizeof(*chl));
       chl->client = client;
       chl->mode = mode;
       chl->channel = channel;
       silc_hash_table_add(channel->user_list, chl->client, chl);
       silc_hash_table_add(client->channels, chl->channel, chl);
       channel->user_count++;
+    } else {
+      /* Update mode */
+      chl->mode = mode;
     }
   }
 }
 
+/* Saves channels and channels user modes to the `client'.  Removes
+   the client from those channels that are not sent in the list but
+   it has joined. */
+
+void silc_server_save_user_channels(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   SilcClientEntry client,
+                                   SilcBuffer channels,
+                                   SilcBuffer channels_user_modes)
+{
+  SilcDList ch;
+  SilcUInt32 *chumodes;
+  SilcChannelPayload entry;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id;
+  SilcChannelClientEntry chl;
+  SilcHashTable ht = NULL;
+  SilcHashTableList htl;
+  char *name;
+  int i = 0;
+
+  if (!channels ||!channels_user_modes)
+    goto out;
+  
+  ch = silc_channel_payload_parse_list(channels->data, channels->len);
+  if (ch && silc_get_mode_list(channels_user_modes, silc_dlist_count(ch),
+                              &chumodes)) {
+    ht = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, 
+                              NULL, NULL, NULL, TRUE);
+    silc_dlist_start(ch);
+    while ((entry = silc_dlist_get(ch)) != SILC_LIST_END) {
+      /* Check if we have this channel, and add it if we don't have it.
+        Also add the client on the channel unless it is there already. */
+      channel_id = silc_channel_get_id_parse(entry);
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel)
+       channel = silc_idlist_find_channel_by_id(server->global_list,
+                                                channel_id, NULL);
+      if (!channel) {
+       if (server->server_type != SILC_SERVER) {
+         silc_free(channel_id);
+         i++;
+         continue;
+       }
+       
+       /* We don't have that channel anywhere, add it. */
+       name = silc_channel_get_name(entry, NULL);
+       channel = silc_idlist_add_channel(server->global_list, strdup(name), 0,
+                                         channel_id, server->router,
+                                         NULL, NULL, 0);
+       if (!channel) {
+         silc_free(channel_id);
+         i++;
+         continue;
+       }
+       channel_id = NULL;
+      }
+
+      channel->mode = silc_channel_get_mode(entry);
+
+      /* Add the client on the channel */
+      if (!silc_server_client_on_channel(client, channel, &chl)) {
+       chl = silc_calloc(1, sizeof(*chl));
+       chl->client = client;
+       chl->mode = chumodes[i++];
+       chl->channel = channel;
+       silc_hash_table_add(channel->user_list, chl->client, chl);
+       silc_hash_table_add(client->channels, chl->channel, chl);
+       channel->user_count++;
+      } else {
+       /* Update mode */
+       chl->mode = chumodes[i++];
+      }
+
+      silc_hash_table_add(ht, channel, channel);
+      silc_free(channel_id);
+    }
+    silc_channel_payload_list_free(ch);
+    silc_free(chumodes);
+  }
+
+ out:
+  /* Go through the list again and remove client from channels that
+     are no part of the list. */
+  if (ht) {
+    silc_hash_table_list(client->channels, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+      if (!silc_hash_table_find(ht, chl->channel, NULL, NULL)) {
+       silc_hash_table_del(chl->channel->user_list, chl->client);
+       silc_hash_table_del(chl->client->channels, chl->channel);
+       silc_free(chl);
+      }
+    }
+    silc_hash_table_list_reset(&htl);
+    silc_hash_table_free(ht);
+  } else {
+    silc_hash_table_list(client->channels, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+      silc_hash_table_del(chl->channel->user_list, chl->client);
+      silc_hash_table_del(chl->client->channels, chl->channel);
+      silc_free(chl);
+    }
+    silc_hash_table_list_reset(&htl);
+  }
+}
+
 /* Lookups route to the client indicated by the `id_data'. The connection
    object and internal data object is returned. Returns NULL if route
    could not be found to the client. If the `client_id' is specified then
    it is used and the `id_data' is ignored. */
 
-SilcSocketConnection silc_server_get_client_route(SilcServer server,
-                                                 unsigned char *id_data,
-                                                 SilcUInt32 id_len,
-                                                 SilcClientID *client_id,
-                                                 SilcIDListData *idata)
+SilcSocketConnection
+silc_server_get_client_route(SilcServer server,
+                            unsigned char *id_data,
+                            SilcUInt32 id_len,
+                            SilcClientID *client_id,
+                            SilcIDListData *idata,
+                            SilcClientEntry *client_entry)
 {
   SilcClientID *id;
   SilcClientEntry client;
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (client_entry)
+    *client_entry = NULL;
+
   /* Decode destination Client ID */
   if (!client_id) {
     id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
@@ -3919,6 +4151,8 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
     /* Seems that client really is directly connected to us */
     if (idata)
       *idata = (SilcIDListData)client;
+    if (client_entry)
+      *client_entry = client;
     return client->connection;
   }
 
@@ -3957,7 +4191,10 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
    Secret channels are not put to the list. */
 
 SilcBuffer silc_server_get_client_channel_list(SilcServer server,
-                                              SilcClientEntry client)
+                                              SilcClientEntry client,
+                                              bool get_private,
+                                              bool get_secret,
+                                              SilcBuffer *user_mode_list)
 {
   SilcBuffer buffer = NULL;
   SilcChannelEntry channel;
@@ -3968,12 +4205,16 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   SilcUInt16 name_len;
   int len;
 
+  if (user_mode_list)
+    *user_mode_list = NULL;
+
   silc_hash_table_list(client->channels, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     channel = chl->channel;
 
-    if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
-       channel->mode & SILC_CHANNEL_MODE_PRIVATE)
+    if (channel->mode & SILC_CHANNEL_MODE_SECRET && !get_secret)
+      continue;
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVATE && !get_private)
       continue;
 
     cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
@@ -3982,23 +4223,37 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
 
     len = 4 + name_len + id_len + 4;
     buffer = silc_buffer_realloc(buffer,
-                                (buffer ? (buffer)->truelen + len : len));
-    silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+                                (buffer ? buffer->truelen + len : len));
+    silc_buffer_pull_tail(buffer, (buffer->end - buffer->data));
     silc_buffer_format(buffer,
                       SILC_STR_UI_SHORT(name_len),
                       SILC_STR_UI_XNSTRING(channel->channel_name,
                                            name_len),
                       SILC_STR_UI_SHORT(id_len),
                       SILC_STR_UI_XNSTRING(cid, id_len),
-                      SILC_STR_UI_INT(chl->mode), /* Client's mode */
+                      SILC_STR_UI_INT(chl->channel->mode),
                       SILC_STR_END);
     silc_buffer_pull(buffer, len);
     silc_free(cid);
+
+    if (user_mode_list) {
+      *user_mode_list = silc_buffer_realloc(*user_mode_list,
+                                           (*user_mode_list ?
+                                            (*user_mode_list)->truelen + 4 :
+                                            4));
+      silc_buffer_pull_tail(*user_mode_list, ((*user_mode_list)->end -
+                                             (*user_mode_list)->data));
+      SILC_PUT32_MSB(chl->mode, (*user_mode_list)->data);
+      silc_buffer_pull(*user_mode_list, 4);
+    }
   }
   silc_hash_table_list_reset(&htl);
 
   if (buffer)
     silc_buffer_push(buffer, buffer->data - buffer->head);
+  if (user_mode_list && *user_mode_list)
+    silc_buffer_push(*user_mode_list, ((*user_mode_list)->data -
+                                      (*user_mode_list)->head));
 
   return buffer;
 }
@@ -4008,6 +4263,7 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
 
 SilcClientEntry silc_server_get_client_resolve(SilcServer server,
                                               SilcClientID *client_id,
+                                              bool always_resolve,
                                               bool *resolved)
 {
   SilcClientEntry client;
@@ -4027,12 +4283,15 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
   if (!client && server->standalone)
     return NULL;
 
-  if (!client || !client->nickname || !client->username) {
+  if (!client || !client->nickname || !client->username ||
+      always_resolve) {
     SilcBuffer buffer, idp;
 
-    client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
-    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
-    client->resolve_cmd_ident = ++server->cmd_ident;
+    if (client) {
+      client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+      client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+      client->resolve_cmd_ident = ++server->cmd_ident;
+    }
 
     idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
@@ -4132,3 +4391,30 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     silc_ske_free(ctx->ske);
   silc_free(ctx);
 }
+
+/* Task callback used to retrieve network statistical information from
+   router server once in a while. */
+
+SILC_TASK_CALLBACK(silc_server_get_stats)
+{
+  SilcServer server = (SilcServer)context;
+  SilcBuffer idp, packet;
+
+  SILC_LOG_DEBUG(("Retrieving stats from router"));
+
+  if (!server->standalone) {
+    idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, 
+                                           ++server->cmd_ident, 1,
+                                           1, idp->data, idp->len);
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_COMMAND, 0, packet->data,
+                           packet->len, FALSE);
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+  }
+
+  silc_schedule_task_add(server->schedule, 0, silc_server_get_stats,
+                        server, 120, 0, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_LOW);
+}