Merged silc_1_0_branch to trunk.
[silc.git] / apps / silcd / server.c
index b0ec5cacc015e844917d26f61387586ae81685f4..2c3875a5c12bc8826866dcbfbe418621fddc5a20 100644 (file)
@@ -4,12 +4,11 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
+  the Free Software Foundation; version 2 of the License.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -31,7 +30,6 @@
 SILC_TASK_CALLBACK(silc_server_rehash_close_connection);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_retry);
 SILC_TASK_CALLBACK(silc_server_connect_router);
-SILC_TASK_CALLBACK(silc_server_connect_to_router);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
 SILC_TASK_CALLBACK(silc_server_accept_new_connection);
@@ -43,9 +41,8 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final);
 SILC_TASK_CALLBACK(silc_server_free_client_data_timeout);
 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);
+SILC_TASK_CALLBACK(silc_server_connect_router);
 
 /* 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
@@ -78,21 +75,26 @@ int silc_server_alloc(SilcServer *new_server)
 
 void silc_server_free(SilcServer server)
 {
+  SilcIDCacheList list;
+  SilcIDCacheEntry cache;
+
   if (!server)
     return;
 
 #ifdef SILC_SIM
   {
     SilcSim sim;
-    
+    silc_dlist_start(server->sim);
     while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
       silc_dlist_del(server->sim, sim);
+      silc_sim_close(sim);
       silc_sim_free(sim);
     }
     silc_dlist_uninit(server->sim);
   }
 #endif
 
+  silc_server_backup_free(server);
   silc_server_config_unref(&server->config_ref);
   if (server->rng)
     silc_rng_free(server->rng);
@@ -107,6 +109,70 @@ void silc_server_free(SilcServer server)
   if (server->id_entry)
     silc_idlist_del_server(server->local_list, server->id_entry);
 
+  /* Delete all channels */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->channels, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_channel(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_channel(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->channels, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_channel(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_channel(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
+  if (server->pk_hash)
+    silc_hash_table_free(server->pk_hash);
+
+  /* Delete all clients */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->clients, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_client(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_client(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->clients, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_client(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_client(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
+
+  /* Delete all servers */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->servers, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_server(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_server(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->servers, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_server(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_server(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
   silc_idcache_free(server->local_list->clients);
   silc_idcache_free(server->local_list->servers);
   silc_idcache_free(server->local_list->channels);
@@ -114,6 +180,7 @@ void silc_server_free(SilcServer server)
   silc_idcache_free(server->global_list->servers);
   silc_idcache_free(server->global_list->channels);
   silc_hash_table_free(server->watcher_list);
+  silc_hash_table_free(server->watcher_list_pk);
 
   silc_hash_free(server->md5hash);
   silc_hash_free(server->sha1hash);
@@ -138,7 +205,7 @@ static bool silc_server_listen(SilcServer server, const char *server_ip,
 {
   *sock = silc_net_create_server(port, server_ip);
   if (*sock < 0) {
-    SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
+    SILC_SERVER_LOG_ERROR(("Could not create server listener: %s on %hu",
                        server_ip, port));
     return FALSE;
   }
@@ -153,7 +220,7 @@ bool silc_server_init_secondary(SilcServer server)
   SilcSocketConnection newsocket = NULL;
   SilcServerConfigServerInfoInterface *interface;
 
-  for (interface = server->config->server_info->secondary; interface; 
+  for (interface = server->config->server_info->secondary; interface;
        interface = interface->next, sock++) {
 
     if (!silc_server_listen(server,
@@ -189,10 +256,10 @@ bool silc_server_init_secondary(SilcServer server)
 
     newsocket->user_data = (void *)server->id_entry;
     silc_schedule_task_add(server->schedule, sock_list[sock],
-                        silc_server_accept_new_connection,
-                        (void *)server, 0, 0,
-                        SILC_TASK_FD,
-                        SILC_TASK_PRI_NORMAL);
+                          silc_server_accept_new_connection,
+                          (void *)server, 0, 0,
+                          SILC_TASK_FD,
+                          SILC_TASK_PRI_NORMAL);
   }
 
   return TRUE;
@@ -222,9 +289,17 @@ bool silc_server_init(SilcServer server)
   server->starttime = time(NULL);
 
   /* Take config object for us */
-  silc_server_config_ref(&server->config_ref, server->config, 
+  silc_server_config_ref(&server->config_ref, server->config,
                         server->config);
 
+#ifdef SILC_DEBUG
+  /* Set debugging on if configured */
+  if (server->config->debug_string) {
+    silc_debug = TRUE;
+    silc_log_set_debug_string(server->config->debug_string);
+  }
+#endif /* SILC_DEBUG */
+
   /* 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;
@@ -257,7 +332,8 @@ bool silc_server_init(SilcServer server)
   silc_pkcs_private_key_set(server->pkcs, server->private_key);
 
   /* Initialize the scheduler */
-  server->schedule = silc_schedule_init(server->config->param.connections_max);
+  server->schedule = silc_schedule_init(server->config->param.connections_max,
+                                       server);
   if (!server->schedule)
     goto err;
 
@@ -266,25 +342,46 @@ bool silc_server_init(SilcServer server)
 
   /* Initialize ID caches */
   server->local_list->clients =
-    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
-  server->local_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
-  server->local_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+                      FALSE, TRUE);
+  server->local_list->servers =
+    silc_idcache_alloc(0, SILC_ID_SERVER, NULL, FALSE, TRUE);
+  server->local_list->channels =
+    silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL, FALSE, TRUE);
 
   /* These are allocated for normal server as well as these hold some
      global information that the server has fetched from its router. For
      router these are used as they are supposed to be used on router. */
   server->global_list->clients =
-    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
-  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_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+                      FALSE, TRUE);
+  server->global_list->servers =
+    silc_idcache_alloc(0, SILC_ID_SERVER, NULL, FALSE, TRUE);
+  server->global_list->channels =
+    silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL, FALSE, TRUE);
+
+  /* Init watcher lists */
+  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;
+  server->watcher_list_pk =
+    silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+                         silc_hash_public_key_compare, NULL,
+                         NULL, NULL, TRUE);
+  if (!server->watcher_list_pk)
+    goto err;
+
+  /* Init public key list */
+  server->pk_hash =
+    silc_hash_table_alloc(0, silc_hash_public_key, NULL,
+                          silc_hash_public_key_compare, NULL,
+                          NULL, NULL, TRUE);
+
+  if (!server->pk_hash)
+    goto err;
 
   /* Create a listening server */
   if (!silc_server_listen(server,
@@ -364,15 +461,8 @@ bool silc_server_init(SilcServer server)
   /* Register protocols */
   silc_server_protocols_register();
 
-  /* 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, 0,
-                        silc_server_connect_to_router,
-                        (void *)server, 0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+  /* Create connections to configured routers. */
+  silc_server_create_connections(server);
 
   /* Add listener task to the scheduler. This task receives new connections
      to the server. This task remains on the queue until the end of the
@@ -385,7 +475,7 @@ bool silc_server_init(SilcServer server)
 
   if (silc_server_init_secondary(server) == FALSE)
     goto err;
-  
+
   server->listenning = TRUE;
 
   /* If server connections has been configured then we must be router as
@@ -411,27 +501,23 @@ bool silc_server_init(SilcServer server)
   /* Clients local list */
   server->purge_i = purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->local_list->clients;
-  purge->schedule = server->schedule;
   purge->timeout = 600;
-  silc_schedule_task_add(purge->schedule, 0,
-                        silc_idlist_purge,
+  silc_schedule_task_add(server->schedule, 0, silc_idlist_purge,
                         (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   /* Clients global list */
   server->purge_g = purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->global_list->clients;
-  purge->schedule = server->schedule;
   purge->timeout = 300;
-  silc_schedule_task_add(purge->schedule, 0,
-                        silc_idlist_purge,
+  silc_schedule_task_add(server->schedule, 0, silc_idlist_purge,
                         (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,
+  if (server->server_type != SILC_ROUTER)
+    silc_schedule_task_add(server->schedule, 0, silc_server_get_stats,
                           server, 10, 0, SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_LOW);
 
@@ -459,7 +545,7 @@ SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
   if (!sock)
     return;
 
-  SILC_LOG_INFO(("Closing connection %s:%d [%s]: connection is unconfigured",
+  SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
                 sock->hostname, sock->port,
                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
@@ -498,24 +584,33 @@ bool silc_server_rehash(SilcServer server)
 
   /* Reinit scheduler if necessary */
   if (newconfig->param.connections_max > server->config->param.connections_max)
-    if (!silc_schedule_reinit(server->schedule, 
+    if (!silc_schedule_reinit(server->schedule,
                              newconfig->param.connections_max))
       return FALSE;
 
   /* 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;
+
+    /* Check server name */
+    server->server_name =
+      silc_identifier_check(newconfig->server_info->server_name,
+                           strlen(newconfig->server_info->server_name),
+                           SILC_STRING_LOCALE, 256, NULL);
+    if (!server->server_name) {
+      SILC_LOG_ERROR(("Malformed server name string '%s'",
+                     server->config->server_info->server_name));
+      return FALSE;
+    }
 
     /* 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, 
+    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,
+                         strdup(server->id_entry->server_name),
                          server->id_entry->id, server->id_entry, 0, NULL))
       return FALSE;
   }
@@ -555,14 +650,15 @@ bool silc_server_rehash(SilcServer server)
 
       /* Check whether new config has this one too */
       for (newptr = newconfig->routers; newptr; newptr = newptr->next) {
-       if (!strcmp(newptr->host, ptr->host) && newptr->port == ptr->port &&
+       if (silc_string_compare(newptr->host, ptr->host) &&
+           newptr->port == ptr->port &&
            newptr->initiator == ptr->initiator) {
          found = TRUE;
          break;
        }
       }
 
-      if (!found) {
+      if (!found && ptr->host) {
        /* Remove this connection */
        SilcSocketConnection sock;
        sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
@@ -586,13 +682,13 @@ bool silc_server_rehash(SilcServer server)
 
       /* Check whether new config has this one too */
       for (newptr = newconfig->servers; newptr; newptr = newptr->next) {
-       if (!strcmp(newptr->host, ptr->host)) {
+       if (silc_string_compare(newptr->host, ptr->host)) {
          found = TRUE;
          break;
        }
       }
 
-      if (!found) {
+      if (!found && ptr->host) {
        /* Remove this connection */
        SilcSocketConnection sock;
        sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_SERVER,
@@ -606,12 +702,38 @@ bool silc_server_rehash(SilcServer server)
     }
   }
 
-  /* 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);
+  if (server->config->clients) {
+    SilcServerConfigClient *ptr;
+    SilcServerConfigClient *newptr;
+    bool found;
+
+    for (ptr = server->config->clients; ptr; ptr = ptr->next) {
+      found = FALSE;
+
+      /* Check whether new config has this one too */
+      for (newptr = newconfig->clients; newptr; newptr = newptr->next) {
+       if (silc_string_compare(newptr->host, ptr->host)) {
+         found = TRUE;
+         break;
+       }
+      }
+
+      if (!found && ptr->host) {
+       /* Remove this connection */
+       SilcSocketConnection sock;
+       sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_CLIENT,
+                                              ptr->host, 0);
+       if (sock)
+         silc_schedule_task_add(server->schedule, sock->sock,
+                                silc_server_rehash_close_connection,
+                                server, 0, 1, SILC_TASK_TIMEOUT,
+                                SILC_TASK_PRI_NORMAL);
+      }
+    }
+  }
+
+  /* Create connections after rehash */
+  silc_server_create_connections(server);
 
   /* Check whether our router status has changed */
   if (newconfig->servers) {
@@ -639,6 +761,14 @@ bool silc_server_rehash(SilcServer server)
   server->config = newconfig;
   silc_server_config_ref(&server->config_ref, server->config, server->config);
 
+#ifdef SILC_DEBUG
+  /* Set debugging on if configured */
+  if (server->config->debug_string) {
+    silc_debug = TRUE;
+    silc_log_set_debug_string(server->config->debug_string);
+  }
+#endif /* SILC_DEBUG */
+
   SILC_LOG_DEBUG(("Server rehashed"));
 
   return TRUE;
@@ -663,7 +793,7 @@ void silc_server_run(SilcServer server)
 
 void silc_server_stop(SilcServer server)
 {
-  SILC_LOG_DEBUG(("Stopping server"));
+  SILC_LOG_INFO(("SILC Server shutting down"));
 
   if (server->schedule) {
     int i;
@@ -675,19 +805,29 @@ void silc_server_stop(SilcServer server)
       if (!server->sockets[i])
        continue;
       if (!SILC_IS_LISTENER(server->sockets[i])) {
-       SilcIDListData idata = server->sockets[i]->user_data;
+       SilcSocketConnection sock = server->sockets[i];
+       SilcIDListData idata = sock->user_data;
 
        if (idata)
          idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
 
        silc_schedule_task_del_by_context(server->schedule,
                                          server->sockets[i]);
-       silc_server_disconnect_remote(server, server->sockets[i], 
-                                     SILC_STATUS_OK, 
+       silc_schedule_task_del_by_fd(server->schedule,
+                                    server->sockets[i]->sock);
+       silc_server_disconnect_remote(server, server->sockets[i],
+                                     SILC_STATUS_OK,
                                      "Server is shutting down");
+       if (server->sockets[i]) {
+         if (sock->user_data)
+           silc_server_free_sock_user_data(server, sock,
+                                           "Server is shutting down");
+         silc_socket_free(sock);
+       }
       } else {
        silc_socket_free(server->sockets[i]);
        server->sockets[i] = NULL;
+        server->stat.conn_num--;
       }
     }
 
@@ -792,12 +932,21 @@ void silc_server_start_key_exchange(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
 {
+  SilcServer server = app_context;
   SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
   SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
   SilcServerConfigConnParams *param =
                (conn->param ? conn->param : &server->config->param);
 
+  /* Don't retry if we are shutting down. */
+  if (server->server_shutdown) {
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+
   SILC_LOG_INFO(("Retrying connecting to a router"));
 
   /* Calculate next timeout */
@@ -810,7 +959,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
   }
   sconn->retry_count++;
   sconn->retry_timeout = sconn->retry_timeout +
-    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
+    (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) &&
@@ -835,15 +984,62 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
+/* Callback for async connection to remote router */
+
+SILC_TASK_CALLBACK(silc_server_connection_established)
+{
+  SilcServer server = app_context;
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  int sock = fd;
+  int opt = EINVAL, optlen = sizeof(opt), ret;
+
+  ret = silc_net_get_socket_opt(sock, SOL_SOCKET, SO_ERROR, &opt, &optlen);
+
+  silc_schedule_task_del_by_fd(server->schedule, sock);
+  silc_schedule_unset_listen_fd(server->schedule, sock);
+
+  if (ret != 0 || opt != 0) {
+    SILC_LOG_ERROR(("Could not connect to router %s:%d: %s",
+                   sconn->remote_host, sconn->remote_port, strerror(opt)));
+    silc_net_close_connection(sock);
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add(server->schedule, 0,
+                            silc_server_connect_to_router_retry,
+                            context, 1, 0, SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+    } else {
+      silc_server_config_unref(&sconn->conn);
+      silc_free(sconn->remote_host);
+      silc_free(sconn->backup_replace_ip);
+      silc_free(sconn);
+    }
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Connection to router %s:%d established", sconn->remote_host,
+                sconn->remote_port));
+
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
+}
+
 /* Generic routine to use connect to a router. */
 
 SILC_TASK_CALLBACK(silc_server_connect_router)
 {
+  SilcServer server = app_context;
   SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
   SilcServerConfigRouter *rconn;
   int sock;
 
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown) {
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+
   SILC_LOG_INFO(("Connecting to the %s %s on port %d",
                 (sconn->backup ? "backup router" : "router"),
                 sconn->remote_host, sconn->remote_port));
@@ -863,25 +1059,34 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
 
   /* Connect to remote host */
-  sock = silc_net_create_connection(
+  sock = silc_net_create_connection_async(
                 (!server->config->server_info->primary ? NULL :
                  server->config->server_info->primary->server_ip),
                 sconn->remote_port, sconn->remote_host);
   if (sock < 0) {
     SILC_LOG_ERROR(("Could not connect to router %s:%d",
                    sconn->remote_host, sconn->remote_port));
-    if (!sconn->no_reconnect)
+    if (!sconn->no_reconnect) {
       silc_schedule_task_add(server->schedule, 0,
                             silc_server_connect_to_router_retry,
-                            context, 0, 1, SILC_TASK_TIMEOUT,
+                            context, 1, 0, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
-    else
+    } else {
       silc_server_config_unref(&sconn->conn);
+      silc_free(sconn->remote_host);
+      silc_free(sconn->backup_replace_ip);
+      silc_free(sconn);
+    }
     return;
   }
 
-  /* Continue with key exchange protocol */
-  silc_server_start_key_exchange(server, sconn, sock);
+  /* wait for the connection to be established */
+  silc_schedule_task_add(server->schedule, sock,
+                        silc_server_connection_established,
+                        context, 0, 0, SILC_TASK_FD,
+                        SILC_TASK_PRI_NORMAL);
+  silc_schedule_set_listen_fd(server->schedule, sock,
+                             SILC_TASK_WRITE, FALSE);
 }
 
 /* This function connects to our primary router or if we are a router this
@@ -889,12 +1094,16 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
    server to do authentication and key exchange with our router - called
    from schedule. */
 
-SILC_TASK_CALLBACK(silc_server_connect_to_router)
+SILC_TASK_CALLBACK_GLOBAL(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
   SilcServerConfigRouter *ptr;
 
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown)
+    return;
+
   SILC_LOG_DEBUG(("We are %s",
                  (server->server_type == SILC_SERVER ?
                   "normal server" : server->server_type == SILC_ROUTER ?
@@ -923,24 +1132,58 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
                    ptr->host, ptr->port));
 
     if (server->server_type == SILC_ROUTER && ptr->backup_router &&
-       ptr->initiator == FALSE && !server->backup_router)
+       ptr->initiator == FALSE && !server->backup_router &&
+       !silc_server_config_get_backup_router(server))
       server->wait_backup = TRUE;
 
     if (ptr->initiator) {
-      /* Check whether we are connected to this host already */
-      if (silc_server_num_sockets_by_remote(server, 
+      /* Check whether we are connecting or 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"));
+
+       /* If we don't have primary router and this connection is our
+          primary router we are in desync.  Reconnect to the primary. */
+       if (server->standalone && !server->router) {
+         SilcServerConfigRouter *primary =
+           silc_server_config_get_primary_router(server);
+         if (primary == ptr) {
+           SilcSocketConnection sock =
+             silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
+                                             ptr->host, ptr->port);
+           if (sock) {
+             server->backup_noswitch = TRUE;
+             if (sock->user_data)
+               silc_server_free_sock_user_data(server, sock, NULL);
+             silc_server_disconnect_remote(server, sock, 0, NULL);
+             server->backup_noswitch = FALSE;
+             SILC_LOG_DEBUG(("Reconnecting to primary router"));
+           } else {
+             continue;
+           }
+         } else {
+           continue;
+         }
+       } else {
+         continue;
+       }
+      }
+      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_UNKNOWN)) {
+       SILC_LOG_DEBUG(("We are already connecting to this router"));
        continue;
       }
 
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
-      sconn->server = server;
       sconn->remote_host = strdup(ptr->host);
       sconn->remote_port = ptr->port;
       sconn->backup = ptr->backup_router;
@@ -988,16 +1231,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
-    silc_server_disconnect_remote(server, sock, 
+    silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
 
     /* Try reconnecting if configuration wants it */
     if (!sconn->no_reconnect) {
       silc_schedule_task_add(server->schedule, 0,
                             silc_server_connect_to_router_retry,
-                            sconn, 0, 1, SILC_TASK_TIMEOUT,
+                            sconn, 1, 0, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
       return;
     }
@@ -1033,16 +1274,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
-    silc_server_disconnect_remote(server, sock, 
+    silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
 
     /* Try reconnecting if configuration wants it */
     if (!sconn->no_reconnect) {
       silc_schedule_task_add(server->schedule, 0,
                             silc_server_connect_to_router_retry,
-                            sconn, 0, 1, SILC_TASK_TIMEOUT,
+                            sconn, 1, 0, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
       return;
     }
@@ -1109,9 +1348,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     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, 
+    silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
     return;
   }
@@ -1158,7 +1395,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcSocketConnection sock = ctx->sock;
   SilcServerEntry id_entry = NULL;
   SilcBuffer packet;
-  SilcServerHBContext hb_context;
   unsigned char *id_string;
   SilcUInt32 id_len;
   SilcIDListData idata;
@@ -1171,14 +1407,16 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_free(ctx->dest_id);
+    sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
                                  NULL);
+    sock->protocol = protocol;
 
     /* Try reconnecting if configuration wants it */
     if (!sconn->no_reconnect) {
       silc_schedule_task_add(server->schedule, 0,
                             silc_server_connect_to_router_retry,
-                            sconn, 0, 1, SILC_TASK_TIMEOUT,
+                            sconn, 1, 0, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
       goto out2;
     }
@@ -1242,8 +1480,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   if (!id_entry) {
     silc_free(ctx->dest_id);
     SILC_LOG_ERROR(("Cannot add new server entry to cache"));
+    sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
                                  NULL);
+    sock->protocol = protocol;
     goto out;
   }
 
@@ -1260,17 +1500,13 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   if (conn && conn->param)
     param = conn->param;
 
-  /* Perform keepalive. The `hb_context' will be freed automatically
-     when finally calling the silc_socket_free function. */
-  hb_context = silc_calloc(1, sizeof(*hb_context));
-  hb_context->server = server;
-  silc_socket_set_heartbeat(sock, param->keepalive_secs, hb_context,
+  /* Perform keepalive. */
+  silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
                            silc_server_perform_heartbeat,
                            server->schedule);
 
   /* Register re-key timeout */
   idata->rekey->timeout = param->key_exchange_rekey;
-  idata->rekey->context = (void *)server;
   silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_rekey_callback,
                         (void *)sock, idata->rekey->timeout, 0,
@@ -1279,28 +1515,40 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   if (!sconn->backup) {
     /* Mark this router our primary router if we're still standalone */
     if (server->standalone) {
+      SILC_LOG_DEBUG(("This connection is our primary router"));
       server->id_entry->router = id_entry;
       server->router = id_entry;
+      server->router->server_type = SILC_ROUTER;
       server->standalone = FALSE;
+      server->backup_primary = FALSE;
 
-      /* If we are router then announce our possible servers.  Backup
-        router announces also global servers. */
-      if (server->server_type == SILC_ROUTER)
-       silc_server_announce_servers(server,
-                                    server->backup_router ? TRUE : FALSE,
-                                    0, SILC_PRIMARY_ROUTE(server));
+      /* Announce data if we are not backup router (unless not as primary
+        currently).  Backup router announces later at the end of
+        resuming protocol. */
+      if (server->backup_router && server->server_type == SILC_ROUTER) {
+       SILC_LOG_DEBUG(("Announce data after resume protocol"));
+      } else {
+       /* If we are router then announce our possible servers.  Backup
+          router announces also global servers. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server,
+                                      server->backup_router ? TRUE : FALSE,
+                                      0, SILC_PRIMARY_ROUTE(server));
 
-      /* Announce our clients and channels to the router */
-      silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
-      silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
+       silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+      }
 
       /* If we are backup router then this primary router is whom we are
         backing up. */
       if (server->server_type == SILC_BACKUP_ROUTER)
-       silc_server_backup_add(server, server->id_entry, sock->ip, 0, TRUE);
+       silc_server_backup_add(server, server->id_entry, sock->ip,
+                              sconn->remote_port, TRUE);
     }
   } else {
     /* Add this server to be our backup router */
+    id_entry->server_type = SILC_BACKUP_ROUTER;
     silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
                           sconn->backup_replace_port, FALSE);
   }
@@ -1310,7 +1558,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
  out:
   /* Call the completion callback to indicate that we've connected to
      the router */
-  if (sconn->callback)
+  if (sconn && sconn->callback)
     (*sconn->callback)(server, id_entry, sconn->callback_context);
 
   /* Free the temporary connection data context */
@@ -1355,8 +1603,6 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   SilcServerConfigDeny *deny;
   int port;
 
-  context = (void *)server;
-
   /* Check whether we could resolve both IP and FQDN. */
   if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
                    server->config->require_reverse_lookup)) {
@@ -1377,13 +1623,14 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
      However, this doesn't set the scheduler for outgoing traffic, it
      will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
      later when outgoing data is available. */
+  context = (void *)server;
   SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
 
   SILC_LOG_INFO(("Incoming connection %s (%s)", sock->hostname,
                 sock->ip));
 
   /* Listenning port */
-  if (!server->sockets[(SilcUInt32)proto_ctx->context]) {
+  if (!server->sockets[SILC_PTR_TO_32(proto_ctx->context)]) {
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_RESOURCE_LIMIT,
                                  "Connection refused");
@@ -1391,7 +1638,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
     silc_free(proto_ctx);
     return;
   }
-  port = server->sockets[(SilcUInt32)proto_ctx->context]->port;
+  port = server->sockets[SILC_PTR_TO_32(proto_ctx->context)]->port;
 
   /* Check whether this connection is denied to connect to us. */
   deny = silc_server_config_find_denied(server, sock->ip);
@@ -1502,6 +1749,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
     silc_net_close_connection(sock);
     return;
   }
+  server->stat.conn_num++;
 
   /* Set socket options */
   silc_net_set_socket_nonblock(sock);
@@ -1517,7 +1765,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      is accepted further. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = server;
-  proto_ctx->context = (void *)fd;
+  proto_ctx->context = SILC_32_TO_PTR(fd);
   silc_socket_host_lookup(newsocket, TRUE,
                          silc_server_accept_new_connection_lookup,
                          (void *)proto_ctx, server->schedule);
@@ -1543,7 +1791,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   if ((protocol->state == SILC_PROTOCOL_STATE_ERROR) ||
       (protocol->state == SILC_PROTOCOL_STATE_FAILURE)) {
     /* Error occured during protocol */
-    SILC_LOG_DEBUG(("Error key exchange protocol"));
+    SILC_LOG_DEBUG(("Error in key exchange protocol"));
     silc_protocol_free(protocol);
     sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
@@ -1556,11 +1804,19 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     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);
-    silc_server_disconnect_remote(server, sock, 
-                                 SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
-                                 NULL);
+
+    if (!SILC_IS_DISCONNECTING(sock)) {
+      SILC_LOG_INFO(("Key exchange failed for %s:%d [%s]", sock->hostname,
+                    sock->port,
+                    (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                     sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                     sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                     "Router")));
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
+                                   NULL);
+    }
+
     server->stat.auth_failures++;
     return;
   }
@@ -1589,9 +1845,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     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);
-    silc_server_disconnect_remote(server, sock, 
+    silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
     server->stat.auth_failures++;
     return;
@@ -1638,7 +1892,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
                           SILC_TASK_PRI_LOW);
 }
 
-/* After this is called, server don't wait for backup router anymore.  
+/* After this is called, server don't wait for backup router anymore.
    This gets called automatically even after we have backup router
    connection established. */
 
@@ -1659,10 +1913,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
-  SilcServerHBContext hb_context;
   SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
   void *id_entry;
-  SilcUInt32 hearbeat_timeout = server->config->param.keepalive_secs;
+  SilcServerConfigConnParams *param = &server->config->param;
 
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
@@ -1679,10 +1932,17 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     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);
-    silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
-                                 NULL);
+
+    if (!SILC_IS_DISCONNECTING(sock)) {
+      SILC_LOG_INFO(("Authentication failed for %s:%d [%s]", sock->hostname,
+                    sock->port,
+                    (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                     sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                     sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                     "Router")));
+      silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
+                                   NULL);
+    }
     server->stat.auth_failures++;
     return;
   }
@@ -1717,17 +1977,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
                                            router->backup_replace_ip, 0)) {
          SILC_LOG_INFO(("Will not accept connections because we do "
                         "not have backup router connection established"));
-         silc_server_disconnect_remote(server, sock, 
+         sock->protocol = NULL;
+         silc_server_disconnect_remote(server, sock,
                                        SILC_STATUS_ERR_PERM_DENIED,
                                        "We do not have connection to backup "
                                        "router established, try later");
          silc_free(sock->user_data);
          server->stat.auth_failures++;
 
-         /* From here on, wait 10 seconds for the backup router to appear. */
+         /* From here on, wait 20 seconds for the backup router to appear. */
          silc_schedule_task_add(server->schedule, 0,
                                 silc_server_backup_router_wait,
-                                (void *)server, 10, 0,
+                                (void *)server, 20, 0,
                                 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
          goto out;
        }
@@ -1745,7 +2006,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
-       silc_server_disconnect_remote(server, sock, 
+       sock->protocol = NULL;
+       silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_AUTH_FAILED, NULL);
        server->stat.auth_failures++;
        goto out;
@@ -1759,10 +2021,31 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       /* Get connection parameters */
       if (conn->param) {
-       if (conn->param->keepalive_secs)
-         hearbeat_timeout = conn->param->keepalive_secs;
+       param = conn->param;
+
+       if (!param->keepalive_secs)
+         param->keepalive_secs = server->config->param.keepalive_secs;
+
+       if (!param->qos && server->config->param.qos) {
+         param->qos = server->config->param.qos;
+         param->qos_rate_limit = server->config->param.qos_rate_limit;
+         param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+         param->qos_limit_sec = server->config->param.qos_limit_sec;
+         param->qos_limit_usec = server->config->param.qos_limit_usec;
+       }
+
+       /* Check if to be anonymous connection */
+       if (param->anonymous)
+         client->mode |= SILC_UMODE_ANONYMOUS;
       }
 
+      /* Add public key to hash list (for whois using attributes) */
+      if (!silc_hash_table_find_by_context(server->pk_hash,
+                                          entry->data.public_key,
+                                          client, NULL))
+       silc_hash_table_add(server->pk_hash,
+                           entry->data.public_key, client);
+
       id_entry = (void *)client;
       break;
     }
@@ -1786,7 +2069,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          !SILC_PRIMARY_ROUTE(server)) {
        SILC_LOG_INFO(("Will not accept server connection because we do "
                       "not have primary router connection established"));
-       silc_server_disconnect_remote(server, sock, 
+       sock->protocol = NULL;
+       silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_PERM_DENIED,
                                      "We do not have connection to primary "
                                      "router established, try later");
@@ -1808,8 +2092,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
        if (rconn) {
          if (rconn->param) {
-           if (rconn->param->keepalive_secs)
-             hearbeat_timeout = rconn->param->keepalive_secs;
+           param = rconn->param;
+
+           if (!param->keepalive_secs)
+             param->keepalive_secs = server->config->param.keepalive_secs;
+
+           if (!param->qos && server->config->param.qos) {
+             param->qos = server->config->param.qos;
+             param->qos_rate_limit = server->config->param.qos_rate_limit;
+             param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+             param->qos_limit_sec = server->config->param.qos_limit_sec;
+             param->qos_limit_usec = server->config->param.qos_limit_usec;
+           }
          }
 
          initiator = rconn->initiator;
@@ -1832,8 +2126,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
        }
        if (sconn) {
          if (sconn->param) {
-           if (sconn->param->keepalive_secs)
-             hearbeat_timeout = sconn->param->keepalive_secs;
+           param = sconn->param;
+
+           if (!param->keepalive_secs)
+             param->keepalive_secs = server->config->param.keepalive_secs;
+
+           if (!param->qos && server->config->param.qos) {
+             param->qos = server->config->param.qos;
+             param->qos_rate_limit = server->config->param.qos_rate_limit;
+             param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+             param->qos_limit_sec = server->config->param.qos_limit_sec;
+             param->qos_limit_usec = server->config->param.qos_limit_usec;
+           }
          }
 
          backup_router = sconn->backup_router;
@@ -1854,17 +2158,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
                                            router->backup_replace_ip, 0)) {
          SILC_LOG_INFO(("Will not accept connections because we do "
                         "not have backup router connection established"));
-         silc_server_disconnect_remote(server, sock, 
+         sock->protocol = NULL;
+         silc_server_disconnect_remote(server, sock,
                                        SILC_STATUS_ERR_PERM_DENIED,
                                        "We do not have connection to backup "
                                        "router established, try later");
          silc_free(sock->user_data);
          server->stat.auth_failures++;
 
-         /* From here on, wait 10 seconds for the backup router to appear. */
+         /* From here on, wait 20 seconds for the backup router to appear. */
          silc_schedule_task_add(server->schedule, 0,
                                 silc_server_backup_router_wait,
-                                (void *)server, 10, 0,
+                                (void *)server, 20, 0,
                                 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
          goto out;
        }
@@ -1899,7 +2204,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       if (!new_server) {
        SILC_LOG_ERROR(("Could not add new server to cache"));
        silc_free(sock->user_data);
-       silc_server_disconnect_remote(server, sock, 
+       sock->protocol = NULL;
+       silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_AUTH_FAILED, NULL);
        server->stat.auth_failures++;
        goto out;
@@ -1916,21 +2222,25 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
        new_server->server_type = SILC_BACKUP_ROUTER;
 
+       SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+                              ("Backup router %s is now online",
+                               sock->hostname));
+
        /* Remove the backup waiting with timeout */
        silc_schedule_task_add(server->schedule, 0,
                               silc_server_backup_router_wait,
-                              (void *)server, 5, 0,
+                              (void *)server, 10, 0,
                               SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
       }
 
       /* Statistics */
       if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER) {
        server->stat.my_servers++;
+       server->stat.servers++;
       } else {
        server->stat.my_routers++;
        server->stat.routers++;
       }
-      server->stat.servers++;
 
       /* Check whether this connection is to be our primary router connection
         if we do not already have the primary route. */
@@ -1966,18 +2276,21 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   /* Connection has been fully established now. Everything is ok. */
   SILC_LOG_DEBUG(("New connection authenticated"));
 
-  /* Perform keepalive. The `hb_context' will be freed automatically
-     when finally calling the silc_socket_free function. */
-  hb_context = silc_calloc(1, sizeof(*hb_context));
-  hb_context->server = server;
-  silc_socket_set_heartbeat(sock, hearbeat_timeout, hb_context,
-                           silc_server_perform_heartbeat,
-                           server->schedule);
+  /* Perform keepalive. */
+  if (param->keepalive_secs)
+    silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
+                             silc_server_perform_heartbeat,
+                             server->schedule);
+
+  /* Perform Quality of Service */
+  if (param->qos)
+    silc_socket_set_qos(sock, param->qos_rate_limit, param->qos_bytes_limit,
+                       param->qos_limit_sec, param->qos_limit_usec,
+                       server->schedule);
 
  out:
-  silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
-  silc_protocol_free(protocol);
+  if (sock->protocol == protocol)
+    silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
@@ -2001,6 +2314,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
   SilcUInt32 sequence = 0;
+  bool local_is_router;
   int ret;
 
   if (!sock) {
@@ -2012,8 +2326,11 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
   if (type == SILC_TASK_WRITE) {
     /* Do not send data to disconnected connection */
-    if (SILC_IS_DISCONNECTED(sock)) {
+    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
       SILC_LOG_DEBUG(("Disconnected socket connection, cannot send"));
+      SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
+      SILC_UNSET_OUTBUF_PENDING(sock);
+      silc_buffer_clear(sock->outbuf);
       return;
     }
 
@@ -2027,6 +2344,14 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (ret == -2)
       return;
 
+    /* The packet has been sent and now it is time to set the connection
+       back to only for input. When there is again some outgoing data
+       available for this connection it will be set for output as well.
+       This call clears the output setting and sets it only for input. */
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+
     if (ret == -1) {
       SILC_LOG_ERROR(("Error sending packet to connection "
                      "%s:%d [%s]", sock->hostname, sock->port,
@@ -2034,17 +2359,21 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
                       sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
                       sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
                       "Router")));
-      return;
-    }
 
-    /* The packet has been sent and now it is time to set the connection
-       back to only for input. When there is again some outgoing data
-       available for this connection it will be set for output as well.
-       This call clears the output setting and sets it only for input. */
-    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
-    SILC_UNSET_OUTBUF_PENDING(sock);
+      if (sock->user_data) {
+       /* If backup then mark that resuming will not be allowed */
+       if (server->server_type == SILC_ROUTER && !server->backup_router &&
+           sock->type == SILC_SOCKET_TYPE_SERVER) {
+         SilcServerEntry server_entry = sock->user_data;
+         if (server_entry->server_type == SILC_BACKUP_ROUTER)
+           server->backup_closed = TRUE;
+       }
 
-    silc_buffer_clear(sock->outbuf);
+       silc_server_free_sock_user_data(server, sock, NULL);
+      }
+      SILC_SET_DISCONNECTING(sock);
+      silc_server_close_connection(server, sock);
+    }
     return;
   }
 
@@ -2054,13 +2383,28 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   ret = silc_packet_receive(sock);
   if (ret < 0) {
 
-    if (ret == -1)
+    if (ret == -1) {
       SILC_LOG_ERROR(("Error receiving packet from connection "
                      "%s:%d [%s] %s", sock->hostname, sock->port,
                      (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                       sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
                       sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
                       "Router"), strerror(errno)));
+
+      if (sock->user_data) {
+       /* If backup then mark that resuming will not be allowed */
+       if (server->server_type == SILC_ROUTER && !server->backup_router &&
+           sock->type == SILC_SOCKET_TYPE_SERVER) {
+         SilcServerEntry server_entry = sock->user_data;
+         if (server_entry->server_type == SILC_BACKUP_ROUTER)
+           server->backup_closed = TRUE;
+       }
+
+       silc_server_free_sock_user_data(server, sock, NULL);
+      }
+      SILC_SET_DISCONNECTING(sock);
+      silc_server_close_connection(server, sock);
+    }
     return;
   }
 
@@ -2078,22 +2422,28 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     }
 
     SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
-    SILC_SET_DISCONNECTING(sock);
 
     if (sock->user_data) {
       char tmp[128];
+
+      /* If backup disconnected then mark that resuming will not be allowed */
+      if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) {
+       SilcServerEntry server_entry = sock->user_data;
+       if (server_entry->server_type == SILC_BACKUP_ROUTER)
+         server->backup_closed = TRUE;
+      }
+
       if (silc_socket_get_error(sock, tmp, sizeof(tmp) - 1))
        silc_server_free_sock_user_data(server, sock, tmp);
       else
        silc_server_free_sock_user_data(server, sock, NULL);
     } else if (server->router_conn && server->router_conn->sock == sock &&
-            !server->router && server->standalone)
-      silc_schedule_task_add(server->schedule, 0,
-                            silc_server_connect_to_router,
-                            server, 1, 0,
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
+              !server->router && server->standalone) {
+      silc_server_create_connections(server);
+    }
 
+    SILC_SET_DISCONNECTING(sock);
     silc_server_close_connection(server, sock);
     return;
   }
@@ -2113,23 +2463,43 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     sequence = idata->psn_receive;
   }
 
-  /* Process the packet. This will call the parser that will then
+  /* Then, process the packet. This will call the parser that will then
      decrypt and parse the packet. */
-  ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
-                                   TRUE : FALSE, cipher, hmac, sequence,
+
+  local_is_router = (server->server_type == SILC_ROUTER);
+
+  /* If socket connection is our primary, we are backup and we are doing
+     backup resuming, we won't process the packet as being a router
+     (affects channel message decryption). */
+  if (server->backup_router && SILC_SERVER_IS_BACKUP(sock) &&
+      SILC_PRIMARY_ROUTE(server) == sock)
+    local_is_router = FALSE;
+
+  ret = silc_packet_receive_process(sock, local_is_router,
+                                   cipher, hmac, sequence,
                                    silc_server_packet_parse, server);
 
-  /* If this socket connection is not authenticated yet and the packet
-     processing failed we will drop the connection since it can be
-     a malicious flooder. */
-  if (sock->type == SILC_SOCKET_TYPE_UNKNOWN && ret == FALSE &&
-      (!sock->protocol || sock->protocol->protocol->type ==
-       SILC_PROTOCOL_SERVER_KEY_EXCHANGE)) {
-    SILC_LOG_DEBUG(("Bad data sent from unknown connection %d", sock->sock));
-    SILC_SET_DISCONNECTING(sock);
+  /* If processing failed the connection is closed. */
+  if (!ret) {
+    /* On packet processing errors we may close our primary router
+       connection but won't become primary router if we are the backup
+       since this is local error condition. */
+    if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
+      server->backup_noswitch = TRUE;
+
+    if (sock->user_data) {
+      /* If we are router and backup errorred then mark that resuming
+        will not be allowed */
+      if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         sock->type == SILC_SOCKET_TYPE_SERVER) {
+       SilcServerEntry server_entry = sock->user_data;
+       if (server_entry->server_type == SILC_BACKUP_ROUTER)
+         server->backup_closed = TRUE;
+      }
 
-    if (sock->user_data)
       silc_server_free_sock_user_data(server, sock, NULL);
+    }
+    SILC_SET_DISCONNECTING(sock);
     silc_server_close_connection(server, sock);
   }
 }
@@ -2145,6 +2515,11 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   SilcIDListData idata = (SilcIDListData)sock->user_data;
   int ret;
 
+  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+    SILC_LOG_DEBUG(("Connection is disconnected"));
+    goto out;
+  }
+
   server->stat.packets_received++;
 
   /* Parse the packet */
@@ -2154,14 +2529,12 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
 
   /* If entry is disabled ignore what we got. */
-  if (ret != SILC_PACKET_RESUME_ROUTER &&
-      idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
-    SILC_LOG_DEBUG(("Connection is disabled"));
-    goto out;
-  }
-  if (ret != SILC_PACKET_HEARTBEAT &&
-      idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
-    SILC_LOG_DEBUG(("Connection is disabled"));
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED &&
+      ret != SILC_PACKET_HEARTBEAT && ret != SILC_PACKET_RESUME_ROUTER &&
+      ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE &&
+      ret != SILC_PACKET_KEY_EXCHANGE_1 && ret != SILC_PACKET_KEY_EXCHANGE_2) {
+    SILC_LOG_DEBUG(("Connection is disabled (packet %s dropped)",
+                   silc_get_packet_name(ret)));
     goto out;
   }
 
@@ -2192,16 +2565,22 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
        packet->dst_id_type == SILC_ID_SERVER &&
        sock->type != SILC_SOCKET_TYPE_CLIENT &&
        memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
+      SilcSocketConnection conn;
 
       /* Route the packet to fastest route for the destination ID */
       void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
                                packet->dst_id_type);
       if (!id)
        goto out;
-      silc_server_packet_route(server,
-                              silc_server_route_get(server, id,
-                                                    packet->dst_id_type),
-                              packet);
+
+      conn = silc_server_route_get(server, id, packet->dst_id_type);
+      if (!conn) {
+       SILC_LOG_WARNING(("Packet to unknown server ID %s, dropped (no route)",
+                         silc_id_render(id, SILC_ID_SERVER)));
+       goto out;
+      }
+
+      silc_server_packet_route(server, conn, packet);
       silc_free(id);
       goto out;
     }
@@ -2237,6 +2616,7 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = parser_context->sock;
   SilcIDListData idata = (SilcIDListData)sock->user_data;
+  bool ret;
 
   if (idata)
     idata->psn_receive = parser_context->packet->sequence + 1;
@@ -2245,26 +2625,45 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
      process all packets synchronously, since there might be packets in
      queue that we are not able to decrypt without first processing the
      packets before them. */
-  if ((parser_context->packet->type == SILC_PACKET_REKEY ||
-       parser_context->packet->type == SILC_PACKET_REKEY_DONE) ||
-      (sock->protocol && sock->protocol->protocol &&
-       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
-       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
-    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+  if (sock->protocol && sock->protocol->protocol &&
+      (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
+    silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
 
+    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+      SILC_LOG_DEBUG(("Connection is disconnected"));
+      return FALSE;
+    }
+
     /* Reprocess data since we'll return FALSE here.  This is because
        the idata->receive_key might have become valid in the last packet
        and we want to call this processor with valid cipher. */
     if (idata)
-      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
-                                 TRUE : FALSE, idata->receive_key,
+      ret = silc_packet_receive_process(
+                                 sock, server->server_type == SILC_ROUTER,
+                                 idata->receive_key,
                                  idata->hmac_receive, idata->psn_receive,
                                  silc_server_packet_parse, server);
     else
-      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
-                                 TRUE : FALSE, NULL, NULL, 0,
+      ret = silc_packet_receive_process(
+                                 sock, server->server_type == SILC_ROUTER,
+                                 NULL, NULL, 0,
                                  silc_server_packet_parse, server);
+
+    if (!ret) {
+      /* On packet processing errors we may close our primary router
+         connection but won't become primary router if we are the backup
+         since this is local error condition. */
+      if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
+       server->backup_noswitch = TRUE;
+
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      SILC_SET_DISCONNECTING(sock);
+      silc_server_close_connection(server, sock);
+    }
+
     return FALSE;
   }
 
@@ -2281,11 +2680,9 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     /* Packets from servers are parsed immediately */
-    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+    silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
     break;
-  default:
-    return TRUE;
   }
 
   return TRUE;
@@ -2322,16 +2719,29 @@ void silc_server_packet_parse_type(SilcServer server,
        message = silc_memdup(packet->buffer->data + 1,
                              packet->buffer->len - 1);
 
-      SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", 
+      SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s",
                     sock->ip, sock->hostname,
                     silc_get_status_message(status), status,
                     message ? message : ""));
       silc_free(message);
 
+      /* Do not switch to backup in case of error */
+      server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
+
+      /* If backup disconnected then mark that resuming will not be allowed */
+      if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) {
+       SilcServerEntry server_entry = sock->user_data;
+       if (server_entry->server_type == SILC_BACKUP_ROUTER)
+         server->backup_closed = TRUE;
+      }
+
       /* Handle the disconnection from our end too */
       if (sock->user_data && SILC_IS_LOCAL(sock->user_data))
        silc_server_free_sock_user_data(server, sock, NULL);
+      SILC_SET_DISCONNECTING(sock);
       silc_server_close_connection(server, sock);
+      server->backup_noswitch = FALSE;
     }
     break;
 
@@ -2355,16 +2765,46 @@ void silc_server_packet_parse_type(SilcServer server,
      */
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
+
+    /* Check for failure START_USE from backup router */
+    if (server->server_type == SILC_SERVER &&
+       server->backup_primary && packet->buffer->len == 4) {
+      SilcUInt32 type;
+      SILC_GET32_MSB(type, packet->buffer->data);
+      if (type == SILC_SERVER_BACKUP_START_USE) {
+       /* Attempt to reconnect to primary */
+       SILC_LOG_DEBUG(("Received failed START_USE from backup %s", sock->ip));
+
+       /* Default action is to disconnect from backup and reconnect to
+          primary.  Since this failure can happen during switching to
+          backup (backup might have not noticed the primary going down yet),
+          we will wait a while and keep sending START_USE to backup.
+          Only after that we'll give up. */
+       if (server->router == sock->user_data &&
+           (time(0) - server->router_connect) < 30) {
+         SILC_LOG_DEBUG(("Resending START_USE to backup router"));
+         silc_server_backup_send_start_use(server, sock, FALSE);
+         break;
+       }
+
+       /* If backup is our primary, disconnect now. */
+       if (server->router == sock->user_data) {
+         if (sock->user_data)
+           silc_server_free_sock_user_data(server, sock, NULL);
+         SILC_SET_DISCONNECTING(sock);
+         silc_server_close_connection(server, sock);
+       }
+
+       /* Reconnect */
+       silc_server_create_connections(server);
+      }
+    }
+
+    /* Execute protocol */
     if (sock->protocol) {
-      SilcServerFailureContext f;
-      f = silc_calloc(1, sizeof(*f));
-      f->server = server;
-      f->sock = sock;
-
-      /* We will wait 5 seconds to process this failure packet */
-      silc_schedule_task_add(server->schedule, sock->sock,
-                        silc_server_failure_callback, (void *)f, 5, 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      break;
     }
     break;
 
@@ -2477,7 +2917,12 @@ void silc_server_packet_parse_type(SilcServer server,
       silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2520,7 +2965,12 @@ void silc_server_packet_parse_type(SilcServer server,
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2563,7 +3013,12 @@ void silc_server_packet_parse_type(SilcServer server,
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2600,7 +3055,12 @@ void silc_server_packet_parse_type(SilcServer server,
       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2699,7 +3159,12 @@ void silc_server_packet_parse_type(SilcServer server,
       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2740,7 +3205,6 @@ void silc_server_create_connection(SilcServer server,
 
   /* Allocate connection object for hold connection specific stuff. */
   sconn = silc_calloc(1, sizeof(*sconn));
-  sconn->server = server;
   sconn->remote_host = strdup(remote_host);
   sconn->remote_port = port;
   sconn->no_reconnect = TRUE;
@@ -2753,7 +3217,21 @@ void silc_server_create_connection(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_close_connection_final)
 {
-  silc_socket_free(context);
+  SilcServer server = app_context;
+  SilcSocketConnection sock = context;
+
+  SILC_LOG_DEBUG(("Deleting socket %p", sock));
+
+  /* Close the actual connection */
+  silc_net_close_connection(sock->sock);
+  server->sockets[sock->sock] = NULL;
+  server->stat.conn_num--;
+
+  /* We won't listen for this connection anymore */
+  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
+  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
+
+  silc_socket_free(sock);
 }
 
 /* Closes connection to socket connection */
@@ -2761,55 +3239,52 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final)
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
-  if (!server->sockets[sock->sock] && SILC_IS_DISCONNECTED(sock)) {
-    silc_schedule_task_add(server->schedule, 0,
+  char tmp[128];
+
+  if (SILC_IS_DISCONNECTED(sock)) {
+    silc_schedule_task_del_by_fd(server->schedule, sock->sock);
+    silc_schedule_task_add(server->schedule, sock->sock,
                           silc_server_close_connection_final,
                           (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_NORMAL);
+    server->sockets[sock->sock] = NULL;
     return;
   }
 
-  SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
-                  sock->port,
-                  (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
-                   sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
-                   sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                   "Router")));
-
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
-
-  /* Unregister all tasks */
-  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
-
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
-  server->sockets[sock->sock] = NULL;
-
-  /* If sock->user_data is NULL then we'll check for active protocols
-     here since the silc_server_free_sock_user_data has not been called
-     for this connection. */
-  if (!sock->user_data) {
-    /* If any protocol is active cancel its execution. It will call
-       the final callback which will finalize the disconnection. */
-    if (sock->protocol) {
-      SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
-      silc_protocol_cancel(sock->protocol, server->schedule);
-      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      silc_protocol_execute_final(sock->protocol, server->schedule);
-      sock->protocol = NULL;
-      return;
-    }
+  /* If any protocol is active cancel its execution. It will call
+     the final callback which will finalize the disconnection. */
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
+    SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
+    silc_protocol_cancel(sock->protocol, server->schedule);
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    sock->protocol = NULL;
+    return;
   }
 
-  silc_schedule_task_add(server->schedule, 0,
+  memset(tmp, 0, sizeof(tmp));
+  silc_socket_get_error(sock, tmp, sizeof(tmp));
+  SILC_LOG_INFO(("Closing connection %s:%d [%s] %s", sock->hostname,
+                sock->port,
+                (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                 sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                 sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                 "Router"), tmp[0] ? tmp : ""));
+
+  SILC_SET_DISCONNECTED(sock);
+  silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
+  silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_close_connection_final,
                         (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
+  server->sockets[sock->sock] = NULL;
 }
 
 /* Sends disconnect message to remote connection and disconnects the
-   connection. */
+   connection.  NOTE: If this is called from protocol callback
+   then sock->protocol must be set NULL before calling this, since
+   this routine dispatches protocol callbacks too. */
 
 void silc_server_disconnect_remote(SilcServer server,
                                   SilcSocketConnection sock,
@@ -2824,6 +3299,12 @@ void silc_server_disconnect_remote(SilcServer server,
   if (!sock)
     return;
 
+  if (SILC_IS_DISCONNECTING(sock)) {
+    SILC_SET_DISCONNECTED(sock);
+    silc_server_close_connection(server, sock);
+    return;
+  }
+
   memset(buf, 0, sizeof(buf));
   va_start(ap, status);
   cp = va_arg(ap, char *);
@@ -2858,22 +3339,19 @@ void silc_server_disconnect_remote(SilcServer server,
   silc_server_packet_queue_purge(server, sock);
 
   /* Mark the connection to be disconnected */
-  SILC_SET_DISCONNECTED(sock);
+  SILC_SET_DISCONNECTING(sock);
   silc_server_close_connection(server, sock);
 }
 
-typedef struct {
-  SilcServer server;
-  SilcClientEntry client;
-} *FreeClientInternal;
-
 SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
 {
-  FreeClientInternal i = (FreeClientInternal)context;
+  SilcServer server = app_context;
+  SilcClientEntry client = context;
 
-  silc_idlist_del_data(i->client);
-  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
-  silc_free(i);
+  assert(!silc_hash_table_count(client->channels));
+
+  silc_idlist_del_data(client);
+  silc_idcache_purge_by_context(server->local_list->clients, client);
 }
 
 /* Frees client data and notifies about client's signoff. */
@@ -2884,20 +3362,8 @@ void silc_server_free_client_data(SilcServer server,
                                  int notify,
                                  const char *signoff)
 {
-  FreeClientInternal i = silc_calloc(1, sizeof(*i));
-
   SILC_LOG_DEBUG(("Freeing client data"));
 
-#if 1
-  if (!client->router && !client->connection &&
-      !(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-    SILC_LOG_ERROR(("****** freeing data for already unregistered client -s"));
-    SILC_LOG_ERROR(("****** Contact Pekka"));
-    SILC_LOG_ERROR(("****** freeing data for already unregistered client -e"));
-    return;
-  }
-#endif
-
   /* If there is pending outgoing data for the client then purge it
      to the network before removing the client entry. */
   silc_server_packet_queue_purge(server, sock);
@@ -2918,14 +3384,19 @@ void silc_server_free_client_data(SilcServer server,
   /* Remove client from all channels */
   if (notify)
     silc_server_remove_from_channels(server, NULL, client,
-                                    TRUE, (char *)signoff, TRUE);
+                                    TRUE, (char *)signoff, TRUE, FALSE);
   else
     silc_server_remove_from_channels(server, NULL, client,
-                                    FALSE, NULL, FALSE);
+                                    FALSE, NULL, FALSE, FALSE);
 
   /* Remove this client from watcher list if it is */
   silc_server_del_from_watcher_list(server, client);
 
+  /* Remove this client from the public key hash list */
+  if (client->data.public_key)
+    silc_hash_table_del_by_context(server->pk_hash,
+                                   client->data.public_key, client);
+
   /* Update statistics */
   server->stat.my_clients--;
   server->stat.clients--;
@@ -2936,17 +3407,23 @@ void silc_server_free_client_data(SilcServer server,
   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 */
-  i->server = server;
-  i->client = client;
-  silc_schedule_task_add(server->schedule, 0,
-                        silc_server_free_client_data_timeout,
-                        (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;
+     into history (for WHOWAS command) for 5 minutes, unless we're
+     shutting down server. */
+  if (!server->server_shutdown) {
+    silc_schedule_task_add(server->schedule, 0,
+                          silc_server_free_client_data_timeout,
+                          client, 600, 0,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
+    client->mode = 0;
+    client->router = NULL;
+    client->connection = NULL;
+  } else {
+    /* Delete directly since we're shutting down server */
+    silc_idlist_del_data(client);
+    silc_idlist_del_client(server->local_list, client);
+  }
 }
 
 /* Frees user_data pointer from socket connection object. This also sends
@@ -2957,6 +3434,19 @@ void silc_server_free_sock_user_data(SilcServer server,
                                     SilcSocketConnection sock,
                                     const char *signoff_message)
 {
+
+  /* If any protocol is active cancel its execution */
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
+    SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
+    silc_protocol_cancel(sock->protocol, server->schedule);
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    sock->protocol = NULL;
+    if (!sock->user_data)
+      return;
+  }
+
   switch (sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
@@ -2976,20 +3466,25 @@ void silc_server_free_sock_user_data(SilcServer server,
       if (user_data->id)
        backup_router = silc_server_backup_get(server, user_data->id);
 
+      if (!server->backup_router && server->server_type == SILC_ROUTER &&
+         backup_router == server->id_entry &&
+         sock->type != SILC_SOCKET_TYPE_ROUTER)
+       backup_router = NULL;
+
+      if (server->server_shutdown || server->backup_noswitch)
+       backup_router = NULL;
+
       /* If this was our primary router connection then we're lost to
         the outside world. */
       if (server->router == user_data) {
        /* Check whether we have a backup router connection */
        if (!backup_router || backup_router == user_data) {
-         silc_schedule_task_add(server->schedule, 0,
-                                silc_server_connect_to_router,
-                                server, 1, 0,
-                                SILC_TASK_TIMEOUT,
-                                SILC_TASK_PRI_NORMAL);
-
+         if (!server->no_reconnect)
+           silc_server_create_connections(server);
          server->id_entry->router = NULL;
          server->router = NULL;
          server->standalone = TRUE;
+         server->backup_primary = FALSE;
          backup_router = NULL;
        } else {
          if (server->id_entry != backup_router) {
@@ -2999,17 +3494,23 @@ void silc_server_free_sock_user_data(SilcServer server,
            server->router = backup_router;
            server->router_connect = time(0);
            server->backup_primary = TRUE;
+           backup_router->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+           /* Send START_USE to backup router to indicate we have switched */
+           silc_server_backup_send_start_use(server,
+                                             backup_router->connection,
+                                             FALSE);
          } else {
            SILC_LOG_INFO(("We are now new primary router in this cell"));
            server->id_entry->router = NULL;
            server->router = NULL;
            server->standalone = TRUE;
-
-           /* We stop here to take a breath */
-           sleep(2);
          }
 
-         if (server->server_type == SILC_BACKUP_ROUTER) {
+         /* We stop here to take a breath */
+         sleep(2);
+
+         if (server->backup_router) {
            server->server_type = SILC_ROUTER;
 
            /* We'll need to constantly try to reconnect to the primary
@@ -3026,23 +3527,39 @@ void silc_server_free_sock_user_data(SilcServer server,
       } else if (backup_router) {
        SILC_LOG_INFO(("Enabling the use of backup router %s",
                       backup_router->server_name));
-       SILC_LOG_DEBUG(("Enabling the use of backup router %s",
-                       backup_router->server_name));
 
        /* Mark this connection as replaced */
        silc_server_backup_replaced_add(server, user_data->id,
                                        backup_router);
+      } else if (server->server_type == SILC_SERVER &&
+                sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       /* Reconnect to the router (backup) */
+       if (!server->no_reconnect)
+         silc_server_create_connections(server);
       }
 
+      if (user_data->server_name)
+       SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+                              ("Server %s signoff", user_data->server_name));
+
       if (!backup_router) {
        /* Remove all servers that are originated from this server, and
           remove the clients of those servers too. */
        silc_server_remove_servers_by_server(server, user_data, TRUE);
 
+#if 0
        /* Remove the clients that this server owns as they will become
-          invalid now too. */
-       silc_server_remove_clients_by_server(server, user_data,
-                                            user_data, TRUE);
+          invalid now too.  For backup router the server is actually
+          coming from the primary router, so mark that as the owner
+          of this entry. */
+       if (server->server_type == SILC_BACKUP_ROUTER &&
+           sock->type == SILC_SOCKET_TYPE_SERVER)
+         silc_server_remove_clients_by_server(server, server->router,
+                                              user_data, TRUE);
+       else
+#endif
+         silc_server_remove_clients_by_server(server, user_data,
+                                              user_data, TRUE);
 
        /* Remove channels owned by this server */
        if (server->server_type == SILC_SERVER)
@@ -3084,13 +3601,14 @@ void silc_server_free_sock_user_data(SilcServer server,
                                 ("%s switched to backup router %s "
                                  "(we are primary router now)",
                                  server->server_name, server->server_name));
-       else
+       else if (server->router)
          SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
                                 SILC_NOTIFY_TYPE_NONE,
                                 ("%s switched to backup router %s",
                                  server->server_name,
                                  server->router->server_name));
       }
+      server->backup_noswitch = FALSE;
 
       /* Free the server entry */
       silc_server_backup_del(server, user_data);
@@ -3100,11 +3618,11 @@ void silc_server_free_sock_user_data(SilcServer server,
        silc_idlist_del_server(server->global_list, user_data);
       if (sock->type == SILC_SOCKET_TYPE_SERVER) {
        server->stat.my_servers--;
+       server->stat.servers--;
       } else {
        server->stat.my_routers--;
        server->stat.routers--;
       }
-      server->stat.servers--;
       if (server->server_type == SILC_ROUTER)
        server->stat.cell_servers--;
 
@@ -3135,15 +3653,6 @@ void silc_server_free_sock_user_data(SilcServer server,
     }
   }
 
-  /* If any protocol is active cancel its execution */
-  if (sock->protocol) {
-    SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
-    silc_protocol_cancel(sock->protocol, server->schedule);
-    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-    silc_protocol_execute_final(sock->protocol, server->schedule);
-    sock->protocol = NULL;
-  }
-
   sock->user_data = NULL;
 }
 
@@ -3156,7 +3665,8 @@ void silc_server_remove_from_channels(SilcServer server,
                                      SilcClientEntry client,
                                      bool notify,
                                      const char *signoff_message,
-                                     bool keygen)
+                                     bool keygen,
+                                     bool killed)
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
@@ -3166,11 +3676,12 @@ void silc_server_remove_from_channels(SilcServer server,
   if (!client)
     return;
 
-  SILC_LOG_DEBUG(("Removing client from joined channels"));
-
   if (notify && !client->id)
     notify = FALSE;
 
+  SILC_LOG_DEBUG(("Removing client %s from joined channels",
+                 notify ? silc_id_render(client->id, SILC_ID_CLIENT) : ""));
+
   if (notify) {
     clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
     if (!clidp)
@@ -3192,7 +3703,7 @@ void silc_server_remove_from_channels(SilcServer server,
     }
 
     silc_hash_table_del(client->channels, channel);
-    silc_hash_table_del(channel->user_list, chl->client);
+    silc_hash_table_del(channel->user_list, client);
     channel->user_count--;
 
     /* If there is no global users on the channel anymore mark the channel
@@ -3201,6 +3712,7 @@ void silc_server_remove_from_channels(SilcServer server,
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
+    memset(chl, 'A', sizeof(*chl));
     silc_free(chl);
 
     /* Update statistics */
@@ -3218,7 +3730,7 @@ void silc_server_remove_from_channels(SilcServer server,
        !silc_server_channel_has_local(channel)) {
       /* Notify about leaving client if this channel has global users. */
       if (notify && channel->global_users)
-       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                           SILC_NOTIFY_TYPE_SIGNOFF,
                                           signoff_message ? 2 : 1,
                                           clidp->data, clidp->len,
@@ -3232,13 +3744,28 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* Send notify to channel about client leaving SILC and channel too */
     if (notify)
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                         SILC_NOTIFY_TYPE_SIGNOFF,
                                         signoff_message ? 2 : 1,
                                         clidp->data, clidp->len,
                                         signoff_message, signoff_message ?
                                         strlen(signoff_message) : 0);
 
+    if (killed && clidp) {
+      /* Remove the client from channel's invite list */
+      if (channel->invite_list &&
+         silc_hash_table_count(channel->invite_list)) {
+       SilcBuffer ab;
+       SilcArgumentPayload iargs;
+       ab = silc_argument_payload_encode_one(NULL, clidp->data,
+                                             clidp->len, 3);
+       iargs = silc_argument_payload_parse(ab->data, ab->len, 1);
+       silc_server_inviteban_process(server, channel->invite_list, 1, iargs);
+       silc_buffer_free(ab);
+       silc_argument_payload_free(iargs);
+      }
+    }
+
     /* Don't create keys if we are shutting down */
     if (server->server_shutdown)
       continue;
@@ -3277,7 +3804,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
   SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Removing %s from channel %s",
-                 silc_id_render(client->id, SILC_ID_CLIENT), 
+                 silc_id_render(client->id, SILC_ID_CLIENT),
                  channel->channel_name));
 
   /* Get the entry to the channel, if this client is not on the channel
@@ -3293,8 +3820,8 @@ bool silc_server_remove_from_one_channel(SilcServer server,
     return FALSE;
   }
 
-  silc_hash_table_del(client->channels, chl->channel);
-  silc_hash_table_del(channel->user_list, chl->client);
+  silc_hash_table_del(client->channels, channel);
+  silc_hash_table_del(channel->user_list, client);
   channel->user_count--;
 
   /* If there is no global users on the channel anymore mark the channel
@@ -3303,6 +3830,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
+  memset(chl, 'O', sizeof(*chl));
   silc_free(chl);
 
   /* Update statistics */
@@ -3324,7 +3852,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
       !silc_server_channel_has_local(channel)) {
     /* Notify about leaving client if this channel has global users. */
     if (notify && channel->global_users)
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                         SILC_NOTIFY_TYPE_LEAVE, 1,
                                         clidp->data, clidp->len);
 
@@ -3336,7 +3864,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
 
   /* Send notify to channel about client leaving the channel */
   if (notify)
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                       SILC_NOTIFY_TYPE_LEAVE, 1,
                                       clidp->data, clidp->len);
 
@@ -3364,7 +3892,8 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
 
   /* If we have protocol active we must assure that we call the protocol's
      final callback so that all the memory is freed. */
-  if (sock->protocol) {
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
     protocol = sock->protocol->protocol->type;
     silc_protocol_cancel(sock->protocol, server->schedule);
     sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -3373,15 +3902,15 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
     return;
   }
 
-  if (sock->user_data)
-    silc_server_free_sock_user_data(server, sock, NULL);
-
-  silc_server_disconnect_remote(server, sock, 
-                               protocol == 
+  silc_server_disconnect_remote(server, sock,
+                               protocol ==
                                SILC_PROTOCOL_SERVER_CONNECTION_AUTH ?
                                SILC_STATUS_ERR_AUTH_FAILED :
                                SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
                                "Connection timeout");
+
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock, NULL);
 }
 
 /* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
@@ -3574,8 +4103,8 @@ silc_server_create_new_channel_with_id(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
 {
+  SilcServer server = app_context;
   SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
-  SilcServer server = (SilcServer)rekey->context;
 
   rekey->task = NULL;
 
@@ -3640,7 +4169,12 @@ bool silc_server_create_channel_key(SilcServer server,
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+    if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      channel->channel_key = NULL;
+      return FALSE;
+    }
   silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash);
   silc_hmac_set_key(channel->hmac, hash,
                    silc_hash_len(silc_hmac_get_hash(channel->hmac)));
@@ -3649,7 +4183,6 @@ bool silc_server_create_channel_key(SilcServer server,
   if (server->server_type == SILC_ROUTER) {
     if (!channel->rekey)
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
-    channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
     channel->rekey->key_len = key_len;
     if (channel->rekey->task)
@@ -3705,8 +4238,9 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     if (!channel) {
       channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
       if (!channel) {
-       SILC_LOG_ERROR(("Received key for non-existent channel %s",
-                       silc_id_render(id, SILC_ID_CHANNEL)));
+       if (server->server_type == SILC_ROUTER)
+         SILC_LOG_ERROR(("Received key for non-existent channel %s",
+                         silc_id_render(id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -3751,7 +4285,12 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+    if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      channel->channel_key = NULL;
+      return FALSE;
+    }
   silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
   silc_hmac_set_key(channel->hmac, hash,
                    silc_hash_len(silc_hmac_get_hash(channel->hmac)));
@@ -3762,7 +4301,6 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   if (server->server_type == SILC_ROUTER) {
     if (!channel->rekey)
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
-    channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
     if (channel->rekey->task)
       silc_schedule_task_del(server->schedule, channel->rekey->task);
@@ -3791,12 +4329,13 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 void silc_server_perform_heartbeat(SilcSocketConnection sock,
                                   void *hb_context)
 {
-  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
+  SilcServer server = hb_context;
 
-  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
+  SILC_LOG_DEBUG(("Sending heartbeat to %s:%d (%s)", sock->hostname,
+                sock->port, sock->ip));
 
   /* Send the heartbeat */
-  silc_server_send_heartbeat(hb->server, sock);
+  silc_server_send_heartbeat(server, sock);
 }
 
 /* Returns assembled of all servers in the given ID list. The packet's
@@ -3926,6 +4465,16 @@ static void silc_server_announce_get_clients(SilcServer server,
            break;
          continue;
        }
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
+       if (!client->connection && !client->router) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
 
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -4028,6 +4577,66 @@ void silc_server_announce_get_channel_topic(SilcServer server,
   }
 }
 
+/* Returns channel's invite and ban lists */
+
+void silc_server_announce_get_inviteban(SilcServer server,
+                                       SilcChannelEntry channel,
+                                       SilcBuffer *invite,
+                                       SilcBuffer *ban)
+{
+  SilcBuffer list, idp, idp2, tmp2;
+  SilcUInt32 type;
+  SilcHashTableList htl;
+  const unsigned char a[1] = { 0x03 };
+
+  idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+
+  /* Encode invite list */
+  if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+    list = silc_buffer_alloc_size(2);
+    type = silc_hash_table_count(channel->invite_list);
+    SILC_PUT16_MSB(type, list->data);
+    silc_hash_table_list(channel->invite_list, &htl);
+    while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+                                              type);
+    silc_hash_table_list_reset(&htl);
+
+    idp2 = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+    *invite =
+      silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_INVITE, 5,
+                                        idp->data, idp->len,
+                                        channel->channel_name,
+                                        strlen(channel->channel_name),
+                                        idp2->data, idp2->len,
+                                        a, 1,
+                                        list->data, list->len);
+    silc_buffer_free(idp2);
+    silc_buffer_free(list);
+  }
+
+  /* Encode ban list */
+  if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+    list = silc_buffer_alloc_size(2);
+    type = silc_hash_table_count(channel->ban_list);
+    SILC_PUT16_MSB(type, list->data);
+    silc_hash_table_list(channel->ban_list, &htl);
+    while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+                                              type);
+    silc_hash_table_list_reset(&htl);
+
+    *ban =
+      silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_BAN, 3,
+                                        idp->data, idp->len,
+                                        a, 1,
+                                        list->data, list->len);
+    silc_buffer_free(list);
+  }
+
+  silc_buffer_free(idp);
+}
+
 /* Returns assembled packets for channel users of the `channel'. */
 
 void silc_server_announce_get_channel_users(SilcServer server,
@@ -4039,32 +4648,43 @@ void silc_server_announce_get_channel_users(SilcServer server,
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
   SilcBuffer chidp, clidp, csidp;
-  SilcBuffer tmp;
+  SilcBuffer tmp, fkey = NULL, chpklist;
   int len;
-  unsigned char mode[4], *fkey = NULL;
-  SilcUInt32 fkey_len = 0;
+  unsigned char mode[4], ulimit[4];
   char *hmac;
 
   SILC_LOG_DEBUG(("Start"));
 
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   csidp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+  chpklist = silc_server_get_channel_pk_list(server, channel, TRUE, FALSE);
 
   /* CMODE notify */
   SILC_PUT32_MSB(channel->mode, mode);
+  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+    SILC_PUT32_MSB(channel->user_limit, ulimit);
   hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
   if (channel->founder_key)
-    fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
-  tmp = 
+    fkey = silc_pkcs_public_key_payload_encode(channel->founder_key);
+  tmp =
     silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
-                                      6, csidp->data, csidp->len,
+                                      8, csidp->data, csidp->len,
                                       mode, sizeof(mode),
                                       NULL, 0,
                                       hmac, hmac ? strlen(hmac) : 0,
                                       channel->passphrase,
                                       channel->passphrase ?
                                       strlen(channel->passphrase) : 0,
-                                      fkey, fkey_len);
+                                      fkey ? fkey->data : NULL,
+                                      fkey ? fkey->len : 0,
+                                      chpklist ? chpklist->data : NULL,
+                                      chpklist ? chpklist->len : 0,
+                                      (channel->mode &
+                                       SILC_CHANNEL_MODE_ULIMIT ?
+                                       ulimit : NULL),
+                                      (channel->mode &
+                                       SILC_CHANNEL_MODE_ULIMIT ?
+                                       sizeof(ulimit) : 0));
   len = tmp->len;
   *channel_modes =
     silc_buffer_realloc(*channel_modes,
@@ -4076,9 +4696,8 @@ void silc_server_announce_get_channel_users(SilcServer server,
   silc_buffer_put(*channel_modes, tmp->data, tmp->len);
   silc_buffer_pull(*channel_modes, len);
   silc_buffer_free(tmp);
-  silc_free(fkey);
+  silc_buffer_free(fkey);
   fkey = NULL;
-  fkey_len = 0;
 
   /* Now find all users on the channel */
   silc_hash_table_list(channel->user_list, &htl);
@@ -4105,12 +4724,13 @@ void silc_server_announce_get_channel_users(SilcServer server,
     /* CUMODE notify for mode change on the channel */
     SILC_PUT32_MSB(chl->mode, mode);
     if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && channel->founder_key)
-      fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
+      fkey = silc_pkcs_public_key_payload_encode(channel->founder_key);
     tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
                                             4, csidp->data, csidp->len,
                                             mode, sizeof(mode),
                                             clidp->data, clidp->len,
-                                            fkey, fkey_len);
+                                            fkey ? fkey->data : NULL,
+                                            fkey ? fkey->len : 0);
     len = tmp->len;
     *channel_users_modes =
       silc_buffer_realloc(*channel_users_modes,
@@ -4123,9 +4743,8 @@ void silc_server_announce_get_channel_users(SilcServer server,
     silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
     silc_buffer_pull(*channel_users_modes, len);
     silc_buffer_free(tmp);
-    silc_free(fkey);
+    silc_buffer_free(fkey);
     fkey = NULL;
-    fkey_len = 0;
     silc_buffer_free(clidp);
   }
   silc_hash_table_list_reset(&htl);
@@ -4145,6 +4764,8 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcBuffer **channel_users_modes,
                                       SilcUInt32 *channel_users_modes_c,
                                       SilcBuffer **channel_topics,
+                                      SilcBuffer **channel_invites,
+                                      SilcBuffer **channel_bans,
                                       SilcChannelID ***channel_ids,
                                       unsigned long creation_time)
 {
@@ -4212,7 +4833,7 @@ void silc_server_announce_get_channels(SilcServer server,
                                      sizeof(**channel_ids) * (i + 1));
          (*channel_ids)[i] = NULL;
          silc_server_announce_get_channel_users(server, channel,
-                                                &(*channel_modes)[i], 
+                                                &(*channel_modes)[i],
                                                 channel_users,
                                                 &(*channel_users_modes)[i]);
          (*channel_ids)[i] = channel->id;
@@ -4223,7 +4844,21 @@ void silc_server_announce_get_channels(SilcServer server,
          (*channel_topics)[i] = NULL;
          silc_server_announce_get_channel_topic(server, channel,
                                                 &(*channel_topics)[i]);
+
+         /* Channel's invite and ban list */
+         *channel_invites = silc_realloc(*channel_invites,
+                                         sizeof(**channel_invites) * (i + 1));
+         (*channel_invites)[i] = NULL;
+         *channel_bans = silc_realloc(*channel_bans,
+                                      sizeof(**channel_bans) * (i + 1));
+         (*channel_bans)[i] = NULL;
+         silc_server_announce_get_inviteban(server, channel,
+                                            &(*channel_invites)[i],
+                                            &(*channel_bans)[i]);
+
          (*channel_users_modes_c)++;
+         silc_free(cid);
+
          i++;
        }
 
@@ -4250,6 +4885,8 @@ void silc_server_announce_channels(SilcServer server,
   SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
   SilcBuffer *channel_topics = NULL;
+  SilcBuffer *channel_invites = NULL;
+  SilcBuffer *channel_bans = NULL;
   SilcUInt32 channel_users_modes_c = 0;
   SilcChannelID **channel_ids = NULL;
 
@@ -4262,6 +4899,8 @@ void silc_server_announce_channels(SilcServer server,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
                                    &channel_topics,
+                                   &channel_invites,
+                                   &channel_bans,
                                    &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
@@ -4272,6 +4911,8 @@ void silc_server_announce_channels(SilcServer server,
                                      &channel_users_modes,
                                      &channel_users_modes_c,
                                      &channel_topics,
+                                     &channel_invites,
+                                     &channel_bans,
                                      &channel_ids, creation_time);
 
   if (channels) {
@@ -4368,26 +5009,98 @@ void silc_server_announce_channels(SilcServer server,
     silc_free(channel_topics);
   }
 
+  if (channel_invites) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_invites[i])
+       continue;
+
+      silc_buffer_push(channel_invites[i],
+                      channel_invites[i]->data -
+                      channel_invites[i]->head);
+      SILC_LOG_HEXDUMP(("channel invite list"), channel_invites[i]->data,
+                      channel_invites[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_invites[i]->data,
+                                  channel_invites[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_invites[i]);
+    }
+    silc_free(channel_invites);
+  }
+
+  if (channel_bans) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_bans[i])
+       continue;
+
+      silc_buffer_push(channel_bans[i],
+                      channel_bans[i]->data -
+                      channel_bans[i]->head);
+      SILC_LOG_HEXDUMP(("channel ban list"), channel_bans[i]->data,
+                      channel_bans[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_bans[i]->data,
+                                  channel_bans[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_bans[i]);
+    }
+    silc_free(channel_bans);
+  }
+
   silc_free(channel_ids);
 }
 
-/* Failure timeout callback. If this is called then we will immediately
-   process the received failure. We always process the failure with timeout
-   since we do not want to blindly trust to received failure packets.
-   This won't be called (the timeout is cancelled) if the failure was
-   bogus (it is bogus if remote does not close the connection after sending
-   the failure). */
+/* Announces WATCH list. */
 
-SILC_TASK_CALLBACK(silc_server_failure_callback)
+void silc_server_announce_watches(SilcServer server,
+                                 SilcSocketConnection remote)
 {
-  SilcServerFailureContext f = (SilcServerFailureContext)context;
+  SilcHashTableList htl;
+  SilcBuffer buffer, idp, args, pkp;
+  SilcClientEntry client;
+  void *key;
 
-  if (f->sock->protocol) {
-    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-    silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
-  }
+  SILC_LOG_DEBUG(("Announcing watch list"));
+
+  /* XXX because way we save the nicks (hash) we cannot announce them. */
+
+  /* XXX we should send all public keys in one command if client is
+     watching more than one key */
+  silc_hash_table_list(server->watcher_list_pk, &htl);
+  while (silc_hash_table_get(&htl, &key, (void *)&client)) {
+    if (!client || !client->id)
+      continue;
 
-  silc_free(f);
+    idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    args = silc_buffer_alloc_size(2);
+    silc_buffer_format(args,
+                      SILC_STR_UI_SHORT(1),
+                      SILC_STR_END);
+    pkp = silc_pkcs_public_key_payload_encode(key);
+    args = silc_argument_payload_encode_one(args, pkp->data, pkp->len, 0x00);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH,
+                                           ++server->cmd_ident, 2,
+                                           1, idp->data, idp->len,
+                                           4, args->data, args->len);
+
+    /* Send command */
+    silc_server_packet_send(server, remote, SILC_PACKET_COMMAND, 0,
+                           buffer->data, buffer->len, TRUE);
+
+    silc_buffer_free(pkp);
+    silc_buffer_free(args);
+    silc_buffer_free(idp);
+    silc_buffer_free(buffer);
+  }
+  silc_hash_table_list_reset(&htl);
 }
 
 /* Assembles user list and users mode list from the `channel'. */
@@ -4462,9 +5175,9 @@ void silc_server_save_users_on_channel(SilcServer server,
   SilcClientEntry client;
   SilcIDCacheEntry cache;
   SilcChannelClientEntry chl;
-  bool global;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Saving %d users on %s channel", user_count,
+                 channel->channel_name));
 
   for (i = 0; i < user_count; i++) {
     /* Client ID */
@@ -4484,21 +5197,19 @@ void silc_server_save_users_on_channel(SilcServer server,
       continue;
     }
 
-    global = FALSE;
+    cache = NULL;
 
     /* Check if we have this client cached already. */
     client = silc_idlist_find_client_by_id(server->local_list, client_id,
                                           server->server_type, &cache);
-    if (!client) {
+    if (!client)
       client = silc_idlist_find_client_by_id(server->global_list,
                                             client_id, server->server_type,
                                             &cache);
-      global = TRUE;
-    }
     if (!client) {
       /* If router did not find such Client ID in its lists then this must
         be bogus client or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER) {
+      if (server->server_type != SILC_SERVER) {
        silc_free(client_id);
        continue;
       }
@@ -4516,15 +5227,18 @@ void silc_server_save_users_on_channel(SilcServer server,
       }
 
       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
-    } else {
-      /* Found, if it is from global list we'll assure that we won't
-        expire it now that the entry is on channel. */
-      if (global)
-       cache->expire = 0;
     }
 
+    if (cache)
+      cache->expire = 0;
     silc_free(client_id);
 
+    if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      SILC_LOG_ERROR(("Attempting to add unregistered client to channel ",
+                     "%s", channel->channel_name));
+      continue;
+    }
+
     if (!silc_server_client_on_channel(client, channel, &chl)) {
       /* Client was not on the channel, add it. */
       chl = silc_calloc(1, sizeof(*chl));
@@ -4562,20 +5276,21 @@ void silc_server_save_user_channels(SilcServer server,
   char *name;
   int i = 0;
 
-  if (!channels ||!channels_user_modes)
+  if (!channels || !channels_user_modes ||
+      !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
     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, 
+    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 = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel)
        channel = silc_idlist_find_channel_by_id(server->global_list,
@@ -4586,7 +5301,7 @@ void silc_server_save_user_channels(SilcServer server,
          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,
@@ -4628,7 +5343,7 @@ void silc_server_save_user_channels(SilcServer server,
      are no part of the list. */
   if (ht) {
     silc_hash_table_list(client->channels, &htl);
-    while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+    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);
@@ -4639,7 +5354,7 @@ void silc_server_save_user_channels(SilcServer server,
     silc_hash_table_free(ht);
   } else {
     silc_hash_table_list(client->channels, &htl);
-    while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+    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);
@@ -4726,7 +5441,7 @@ silc_server_get_client_route(SilcServer server,
       dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
 
       silc_free(id);
-      if (idata)
+      if (idata && dst_sock)
        *idata = (SilcIDListData)dst_sock->user_data;
       return dst_sock;
     }
@@ -4807,77 +5522,98 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   return buffer;
 }
 
-/* Finds client entry by Client ID and if it is not found then resolves
-   it using WHOIS command. */
+/* Timeout callback for unsuccessful rekey.  The rekey did not go through
+   for some reason. */
 
-SilcClientEntry silc_server_get_client_resolve(SilcServer server,
-                                              SilcClientID *client_id,
-                                              bool always_resolve,
-                                              bool *resolved)
+SILC_TASK_CALLBACK(silc_server_rekey_timeout)
 {
-  SilcClientEntry client;
-
-  if (resolved)
-    *resolved = FALSE;
-
-  client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                        TRUE, NULL);
-  if (!client) {
-    client = silc_idlist_find_client_by_id(server->global_list,
-                                          client_id, TRUE, NULL);
-    if (!client && server->server_type == SILC_ROUTER)
-      return NULL;
-  }
-
-  if (!client && server->standalone)
-    return NULL;
-
-  if (!client || !client->nickname || !client->username ||
-      always_resolve) {
-    SilcBuffer buffer, idp;
-
-    if (client) {
-      client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
-      client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
-      client->resolve_cmd_ident = ++server->cmd_ident;
-    }
+  SilcServerRekeyInternalContext *ctx = context;
+  SilcServer server = app_context;
+  SilcSocketConnection sock = ctx->sock;
 
-    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);
-    silc_server_packet_send(server, client ? client->router->connection :
-                           SILC_PRIMARY_ROUTE(server),
-                           SILC_PACKET_COMMAND, 0,
-                           buffer->data, buffer->len, FALSE);
-    silc_buffer_free(idp);
-    silc_buffer_free(buffer);
+  SILC_LOG_DEBUG(("Timeout occurred in rekey protocol with %s:%d [%s]",
+                 sock->hostname, sock->port,
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
 
-    if (resolved)
-      *resolved = TRUE;
+  SILC_LOG_WARNING(("Timeout occurred in rekey protocol with %s:%d [%s]",
+                   sock->hostname, sock->port,
+                   (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                    sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                    sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                    "Router")));
 
-    return NULL;
+  if (sock->protocol) {
+    silc_protocol_cancel(sock->protocol, server->schedule);
+    silc_protocol_free(sock->protocol);
+    sock->protocol = NULL;
   }
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_socket_free(sock);
+  silc_free(ctx);
+
+  /* Disconnect since we failed to rekey, the keys are probably wrong. */
+  silc_server_disconnect_remote(server, sock,
+                               SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock, NULL);
 
-  return client;
+  /* Reconnect */
+  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+    silc_server_create_connections(server);
 }
 
 /* A timeout callback for the re-key. We will be the initiator of the
    re-key protocol. */
 
-SILC_TASK_CALLBACK(silc_server_rekey_callback)
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_callback)
 {
+  SilcServer server = app_context;
   SilcSocketConnection sock = (SilcSocketConnection)context;
   SilcIDListData idata = (SilcIDListData)sock->user_data;
-  SilcServer server = (SilcServer)idata->rekey->context;
   SilcProtocol protocol;
   SilcServerRekeyInternalContext *proto_ctx;
 
+  if (!idata)
+    return;
+
+  /* Do not execute rekey with disabled connections, as it would not
+     go through anyway. */
+  if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
+
+  /* If rekey protocol is active already wait for it to finish */
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)
+    return;
+
+  /* If any other protocol is active do not start this protocol yet. */
+  if (sock->protocol) {
+    SILC_LOG_DEBUG(("Waiting for other protocol to finish before rekeying"));
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_rekey_callback,
+                          sock, 60, 0, SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s]",
+                 sock->hostname, sock->port,
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
+
   /* Allocate internal protocol context. This is sent as context
      to the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
-  proto_ctx->sock = sock;
+  proto_ctx->sock = silc_socket_dup(sock);
   proto_ctx->responder = FALSE;
   proto_ctx->pfs = idata->rekey->pfs;
 
@@ -4887,16 +5623,20 @@ SILC_TASK_CALLBACK(silc_server_rekey_callback)
                      &protocol, proto_ctx, silc_server_rekey_final);
   sock->protocol = protocol;
 
+  /* Register timeout callback in case the rekey does not go through. */
+  proto_ctx->timeout_task =
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_rekey_timeout,
+                          proto_ctx,
+                          (idata->rekey->timeout >
+                           server->config->key_exchange_timeout ?
+                           idata->rekey->timeout :
+                           server->config->key_exchange_timeout * 4), 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
+
   /* Run the protocol */
   silc_protocol_execute(protocol, server->schedule, 0, 0);
-
-  SILC_LOG_DEBUG(("Rekey protocol completed"));
-
-  /* Re-register re-key timeout */
-  silc_schedule_task_add(server->schedule, sock->sock,
-                        silc_server_rekey_callback,
-                        context, idata->rekey->timeout, 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* The final callback for the REKEY protocol. This will actually take the
@@ -4909,13 +5649,16 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     (SilcServerRekeyInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
 
-  SILC_LOG_DEBUG(("Start"));
+  if (ctx->timeout_task)
+    silc_schedule_task_del(server->schedule, ctx->timeout_task);
 
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    SILC_LOG_ERROR(("Error occurred during rekey protocol"));
+    SILC_LOG_ERROR(("Error occurred during rekey protocol with "
+                   "%s (%s)", sock->hostname, sock->ip));
     silc_protocol_cancel(protocol, server->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
@@ -4923,14 +5666,37 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
+    silc_socket_free(sock);
     silc_free(ctx);
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
+
+    /* Reconnect */
+    if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+      silc_server_create_connections(server);
     return;
   }
 
+  SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]",
+                 sock->hostname, sock->port,
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
+
   /* Purge the outgoing data queue to assure that all rekey packets really
      go to the network before we quit the protocol. */
   silc_server_packet_queue_purge(server, sock);
 
+  /* Re-register re-key timeout */
+  if (ctx->responder == FALSE)
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_rekey_callback,
+                          sock, idata->rekey->timeout, 0,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
@@ -4938,6 +5704,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  silc_socket_free(sock);
   silc_free(ctx);
 }
 
@@ -4949,11 +5716,10 @@ SILC_TASK_CALLBACK(silc_server_get_stats)
   SilcServer server = (SilcServer)context;
   SilcBuffer idp, packet;
 
-  SILC_LOG_DEBUG(("Retrieving stats from router"));
-
   if (!server->standalone) {
+    SILC_LOG_DEBUG(("Retrieving stats from router"));
     idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
-    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, 
+    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
                                            ++server->cmd_ident, 1,
                                            1, idp->data, idp->len);
     silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),