updates.
[silc.git] / apps / silcd / server.c
index 7873aff855dc526b5a3b2d83ac86de52796849f3..5a0a9ebd5d39e778ae0e7a237fe73a2679d246f4 100644 (file)
@@ -4,13 +4,13 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2002 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.
-  
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -19,7 +19,7 @@
 */
 /*
  * This is the actual SILC server than handles everything relating to
- * servicing the SILC connections. This is also a SILC router as a router 
+ * servicing the SILC connections. This is also a SILC router as a router
  * is also normal server.
  */
 /* $Id$ */
@@ -28,6 +28,7 @@
 #include "server_internal.h"
 
 /* Static prototypes */
+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);
@@ -37,7 +38,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second);
 SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
 SILC_TASK_CALLBACK(silc_server_packet_process);
 SILC_TASK_CALLBACK(silc_server_packet_parse_real);
+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);
 
@@ -74,17 +78,8 @@ void silc_server_free(SilcServer server)
 {
   if (server) {
 #ifdef SILC_SIM
-    SilcSimContext *sim;
-#endif
-
-    if (server->local_list)
-      silc_free(server->local_list);
-    if (server->global_list)
-      silc_free(server->global_list);
-    if (server->rng)
-      silc_rng_free(server->rng);
+    SilcSim sim;
 
-#ifdef SILC_SIM
     while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
       silc_dlist_del(server->sim, sim);
       silc_sim_free(sim);
@@ -92,66 +87,85 @@ void silc_server_free(SilcServer server)
     silc_dlist_uninit(server->sim);
 #endif
 
-    if (server->params)
-      silc_free(server->params);
-
+    silc_server_config_unref(&server->config_ref);
+    if (server->rng)
+      silc_rng_free(server->rng);
+    if (server->pkcs)
+      silc_pkcs_free(server->pkcs);
+    if (server->public_key)
+      silc_pkcs_public_key_free(server->public_key);
+    if (server->private_key)
+      silc_pkcs_private_key_free(server->private_key);
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
+    if (server->id_entry)
+      silc_idlist_del_server(server->local_list, server->id_entry);
 
+    silc_idcache_free(server->local_list->clients);
+    silc_idcache_free(server->local_list->servers);
+    silc_idcache_free(server->local_list->channels);
+    silc_idcache_free(server->global_list->clients);
+    silc_idcache_free(server->global_list->servers);
+    silc_idcache_free(server->global_list->channels);
+
+    silc_free(server->sockets);
     silc_free(server);
   }
 }
 
+/* Opens a listening port.
+   XXX This function will become more general and will support multiple
+   listening ports */
+
+static bool silc_server_listen(SilcServer server, int *sock)
+{
+
+  *sock = silc_net_create_server(server->config->server_info->port,
+                               server->config->server_info->server_ip);
+  if (*sock < 0) {
+    SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
+                   server->config->server_info->server_ip,
+                   server->config->server_info->port));
+    return FALSE;
+  }
+  return TRUE;
+}
+
 /* Initializes the entire SILC server. This is called always before running
    the server. This is called only once at the initialization of the program.
-   This binds the server to its listenning port. After this function returns 
-   one should call silc_server_run to start the server. This returns TRUE 
+   This binds the server to its listenning port. After this function returns
+   one should call silc_server_run to start the server. This returns TRUE
    when everything is ok to run the server. Configuration file must be
    read and parsed before calling this. */
 
-int silc_server_init(SilcServer server)
+bool silc_server_init(SilcServer server)
 {
-  int *sock = NULL, sock_count = 0, i;
+  int sock;
   SilcServerID *id;
   SilcServerEntry id_entry;
   SilcIDListPurge purge;
+  SilcSocketConnection newsocket = NULL;
 
   SILC_LOG_DEBUG(("Initializing server"));
-  assert(server);
-  assert(server->config);
-
-  /* Set public and private keys */
-  if (!server->config->server_keys ||
-      !server->config->server_keys->public_key || 
-      !server->config->server_keys->private_key) {
-    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
-    return FALSE;
-  }
-  server->public_key = server->config->server_keys->public_key;
-  server->private_key = server->config->server_keys->private_key;
-
-  /* XXX After server is made as Silc Server Library this can be given
-     as argument, for now this is hard coded */
-  server->params = silc_calloc(1, sizeof(*server->params));
-  server->params->retry_count = SILC_SERVER_RETRY_COUNT;
-  server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN;
-  server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
-  server->params->retry_keep_trying = FALSE;
-  server->params->protocol_timeout = 60;
-  server->params->require_reverse_mapping = FALSE;
-
-  /* Set log files where log message should be saved. */
-  server->config->server = server;
-  silc_server_config_setlogfiles(server->config);
+
+  /* Take config object for us */
+  silc_server_config_ref(&server->config_ref, server->config, 
+                        server->config);
+
+  /* Steal public and private key from the config object */
+  server->public_key = server->config->server_info->public_key;
+  server->private_key = server->config->server_info->private_key;
+  server->config->server_info->public_key = NULL;
+  server->config->server_info->private_key = NULL;
+
   /* Register all configured ciphers, PKCS and hash functions. */
-  if (!silc_server_config_register_ciphers(server->config))
+  if (!silc_server_config_register_ciphers(server))
     silc_cipher_register_default();
-  if (!silc_server_config_register_pkcs(server->config))
+  if (!silc_server_config_register_pkcs(server))
     silc_pkcs_register_default();
-  if (!silc_server_config_register_hashfuncs(server->config))
+  if (!silc_server_config_register_hashfuncs(server))
     silc_hash_register_default();
-  if (!silc_server_config_register_hmacs(server->config))
+  if (!silc_server_config_register_hmacs(server))
     silc_hmac_register_default();
 
   /* Initialize random number generator for the server. */
@@ -163,142 +177,143 @@ int silc_server_init(SilcServer server)
   silc_hash_alloc("md5", &server->md5hash);
   silc_hash_alloc("sha1", &server->sha1hash);
 
-  /* Initialize none cipher */
-  silc_cipher_alloc("none", &server->none_cipher);
-
-  /* Create a listening server. Note that our server can listen on
-     multiple ports. All listeners are created here and now. */
-  /* XXX Still check this whether to use server_info or listen_port. */
-  sock_count = 0;
-  while(server->config->listen_port) {
-    int tmp;
+  /* Allocate PKCS context for local public and private keys */
+  if (!silc_pkcs_alloc(server->public_key->name, &server->pkcs))
+    goto err;
+  silc_pkcs_public_key_set(server->pkcs, server->public_key);
+  silc_pkcs_private_key_set(server->pkcs, server->private_key);
 
-    tmp = silc_net_create_server(server->config->listen_port->port,
-                                server->config->listen_port->host);
-    if (tmp < 0)
-      goto err0;
+  /* Initialize the scheduler */
+  server->schedule = silc_schedule_init(server->config->param.connections_max);
+  if (!server->schedule)
+    goto err;
 
-    sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
-    sock[sock_count] = tmp;
-    server->config->listen_port = server->config->listen_port->next;
-    sock_count++;
-  }
+  /* First, register log files configuration for error output */
+  silc_server_config_setlogfiles(server);
 
   /* Initialize ID caches */
-  server->local_list->clients = 
+  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);
 
-  /* These are allocated for normal server as well as these hold some 
-     global information that the server has fetched from its router. For 
+  /* 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 = 
+  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);
 
-  /* Allocate the entire socket list that is used in server. Eventually 
-     all connections will have entry in this table (it is a table of 
-     pointers to the actual object that is allocated individually 
+  /* Create a listening server */
+  if (!silc_server_listen(server, &sock))
+    goto err;
+
+  /* Set socket to non-blocking mode */
+  silc_net_set_socket_nonblock(sock);
+  server->sock = sock;
+
+  /* Allocate the entire socket list that is used in server. Eventually
+     all connections will have entry in this table (it is a table of
+     pointers to the actual object that is allocated individually
      later). */
-  server->sockets = silc_calloc(SILC_SERVER_MAX_CONNECTIONS,
+  server->sockets = silc_calloc(server->config->param.connections_max,
                                sizeof(*server->sockets));
+  if (!server->sockets)
+    goto err;
 
-  for (i = 0; i < sock_count; i++) {
-    SilcSocketConnection newsocket = NULL;
+  /* Add ourselves also to the socket table. The entry allocated above
+     is sent as argument for fast referencing in the future. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
 
-    /* Set socket to non-blocking mode */
-    silc_net_set_socket_nonblock(sock[i]);
-    server->sock = sock[i];
-    
-    /* Create a Server ID for the server. */
-    silc_id_create_server_id(sock[i], server->rng, &id);
-    if (!id) {
-      goto err0;
+  /* Perform name and address lookups to resolve the listenning address
+     and port. */
+  if (!silc_net_check_local_by_sock(sock, &newsocket->hostname,
+                                   &newsocket->ip)) {
+    if ((server->config->require_reverse_lookup && !newsocket->hostname) ||
+       !newsocket->ip) {
+      SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+                     newsocket->hostname ? newsocket->hostname :
+                     newsocket->ip ? newsocket->ip : ""));
+      server->stat.conn_failures++;
+      goto err;
     }
-    
-    server->id = id;
-    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
-    server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
-    server->id_type = SILC_ID_SERVER;
-    server->server_name = server->config->server_info->server_name;
-
-    /* Add ourselves to the server list. We don't have a router yet 
-       beacuse we haven't established a route yet. It will be done later. 
-       For now, NULL is sent as router. This allocates new entry to
-       the ID list. */
-    id_entry = 
-      silc_idlist_add_server(server->local_list,
-                            server->config->server_info->server_name,
-                            server->server_type, server->id, NULL, NULL);
-    if (!id_entry) {
-      SILC_LOG_ERROR(("Could not add ourselves to cache"));
-      goto err0;
-    }
-    
-    /* Add ourselves also to the socket table. The entry allocated above
-       is sent as argument for fast referencing in the future. */
-    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, 
-                     &newsocket);
-
-    server->sockets[sock[i]] = newsocket;
-    
-    /* Perform name and address lookups to resolve the listenning address
-       and port. */
-    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
-                                    &newsocket->ip)) {
-      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-         !newsocket->ip) {
-       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
-                       newsocket->hostname ? newsocket->hostname :
-                       newsocket->ip ? newsocket->ip : ""));
-       server->stat.conn_failures++;
-       goto err0;
-      }
-      if (!newsocket->hostname)
-       newsocket->hostname = strdup(newsocket->ip);
-    }
-    newsocket->port = silc_net_get_local_port(sock[i]);
-
-    /* Put the allocated socket pointer also to the entry allocated above 
-       for fast back-referencing to the socket list. */
-    id_entry->connection = (void *)server->sockets[sock[i]];
-    server->id_entry = id_entry;
+    if (!newsocket->hostname)
+      newsocket->hostname = strdup(newsocket->ip);
   }
+  newsocket->port = silc_net_get_local_port(sock);
+
+  /* Create a Server ID for the server. */
+  silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
+  if (!id)
+    goto err;
+
+  server->id = id;
+  server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
+  server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
+  server->id_type = SILC_ID_SERVER;
+  server->server_name = server->config->server_info->server_name;
+  server->config->server_info->server_name = NULL;
+
+  /* Add ourselves to the server list. We don't have a router yet
+     beacuse we haven't established a route yet. It will be done later.
+     For now, NULL is sent as router. This allocates new entry to
+     the ID list. */
+  id_entry =
+    silc_idlist_add_server(server->local_list, strdup(server->server_name),
+                          server->server_type, server->id, NULL, NULL);
+  if (!id_entry) {
+    SILC_LOG_ERROR(("Could not add ourselves to cache"));
+    goto err;
+  }
+  id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+  /* Put the allocated socket pointer also to the entry allocated above
+     for fast back-referencing to the socket list. */
+  newsocket->user_data = (void *)id_entry;
+  id_entry->connection = (void *)newsocket;
+  server->id_entry = id_entry;
 
   /* Register protocols */
   silc_server_protocols_register();
 
-  /* Initialize the scheduler. */
-  server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
-  if (!server->schedule)
-    goto err0;
-  
   /* Add the first task to the scheduler. This is task that is executed by
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  silc_schedule_task_add(server->schedule, sock[0], 
+  silc_schedule_task_add(server->schedule, 0,
                         silc_server_connect_to_router,
                         (void *)server, 0, 1,
                         SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
 
   /* 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 
+     to the server. This task remains on the queue until the end of the
      program. */
-  silc_schedule_task_add(server->schedule, sock[0],
+  silc_schedule_task_add(server->schedule, sock,
                         silc_server_accept_new_connection,
-                        (void *)server, 0, 0, 
+                        (void *)server, 0, 0,
                         SILC_TASK_FD,
                         SILC_TASK_PRI_NORMAL);
   server->listenning = TRUE;
 
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
-  if (server->config->servers)
+  if (server->config->servers) {
+    SilcServerConfigServer *ptr = server->config->servers;
+
     server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       server->id_entry->server_type = SILC_BACKUP_ROUTER;
+       break;
+      }
+      ptr = ptr->next;
+    }
+  }
 
   /* Register the ID Cache purge task. This periodically purges the ID cache
      and removes the expired cache entries. */
@@ -307,18 +322,20 @@ int silc_server_init(SilcServer server)
   purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->local_list->clients;
   purge->schedule = server->schedule;
-  silc_schedule_task_add(purge->schedule, 0, 
+  purge->timeout = 600;
+  silc_schedule_task_add(purge->schedule, 0,
                         silc_idlist_purge,
-                        (void *)purge, 600, 0,
+                        (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   /* Clients global list */
   purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->global_list->clients;
   purge->schedule = server->schedule;
-  silc_schedule_task_add(purge->schedule, 0, 
+  purge->timeout = 300;
+  silc_schedule_task_add(purge->schedule, 0,
                         silc_idlist_purge,
-                        (void *)purge, 300, 0,
+                        (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   SILC_LOG_DEBUG(("Server initialized"));
@@ -326,141 +343,121 @@ int silc_server_init(SilcServer server)
   /* We are done here, return succesfully */
   return TRUE;
 
- err0:
-  for (i = 0; i < sock_count; i++)
-    silc_net_close_server(sock[i]);
-
+ err:
+  silc_net_close_server(sock);
   return FALSE;
 }
 
-/* Fork server to background and set gid+uid to non-root.
-   Silcd will not run as root, so trying to set either user or group to
-   root will cause silcd to exit. */
+/* This function basically reads the config file again and switches the config
+   object pointed by the server object. After that, we have to fix various
+   things such as the server_name and the listening ports.
+   Keep in mind that we no longer have the root privileges at this point. */
 
-void silc_server_daemonise(SilcServer server)
+bool silc_server_rehash(SilcServer server)
 {
-  /* Are we executing silcd as root or a regular user? */
-  if (geteuid()==0) {
-    
-    struct passwd *pw;
-    struct group *gr;
-    char *user, *group;
-    
-    if (!server->config->identity || !server->config->identity->user || 
-       !server->config->identity->group) {
-      fprintf(stderr, "Error:"
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the [Identity] configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    }
-    
-    /* Get the values given for user and group in configuration file */
-    user=server->config->identity->user;
-    group=server->config->identity->group;
-    
-    /* Check whether the user/group information is text */ 
-    if (atoi(user)!=0 || atoi(group)!=0) {
-      SILC_LOG_DEBUG(("Invalid user and/or group information"));
-      SILC_LOG_DEBUG(("User and/or group given as number"));
-      fprintf(stderr, "Invalid user and/or group information\n");
-      fprintf(stderr, "Please assign them as names, not numbers\n");
-      exit(1);
-    }
-    
-    /* Catch the nasty incident of string "0" returning 0 from atoi */
-    if (strcmp("0", user)==0 || strcmp("0", group)==0) {
-      SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
-      fprintf(stderr, "User and/or group configured to 0. Exiting\n");
-      exit(1);
-    }
-    
-    pw=getpwnam(user);
-    gr=getgrnam(group);
+  SilcServerConfig newconfig;
+  SilcUInt32 max_conns;
 
-    if (!pw) {
-      fprintf(stderr, "No such user %s found\n", user);
-      exit(1);
-    }
+  SILC_LOG_INFO(("Rehashing server"));
 
-    if (!gr) {
-      fprintf(stderr, "No such group %s found\n", group);
-      exit(1);
-    }
-    
-    /* Check whether user and/or group is set to root. If yes, exit
-       immediately. Otherwise, setgid and setuid server to user.group */
-    if (gr->gr_gid==0 || pw->pw_uid==0) {
-      fprintf(stderr, "Error:"
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the [Identity] configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    } else {
-      /* Fork server to background, making it a daemon */
-      if (fork()) {
-        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
-        SILC_LOG_DEBUG(("Forking SILC server to background"));
-        exit(0);
-      } 
-      setsid();
-      
-      SILC_LOG_DEBUG(("Changing to group %s", group));
-      if(setgid(gr->gr_gid)==0) {
-        SILC_LOG_DEBUG(("Setgid to %s", group));
-      } else {
-        SILC_LOG_DEBUG(("Setgid to %s failed", group));
-        fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
-                group);
-        exit(1);
-      }
-      SILC_LOG_DEBUG(("Changing to user nobody"));
-      if(setuid(pw->pw_uid)==0) {
-        SILC_LOG_DEBUG(("Setuid to %s", user));
-      } else {
-        SILC_LOG_DEBUG(("Setuid to %s failed", user));
-        fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
-                user);
-        exit(1);
-      }
-    }
-  } else {
-    /* Fork server to background, making it a daemon */
-    if (fork()) {
-      SILC_LOG_DEBUG(("Server started as user")); 
-      SILC_LOG_DEBUG(("Forking SILC server to background"));
-      exit(0);
-    }
-    setsid();
+  /* Reset the logging system */
+  silc_log_quick = TRUE;
+  silc_log_flush_all();
+
+  /* Start the main rehash phase (read again the config file) */
+  newconfig = silc_server_config_alloc(server->config_file);
+  if (!newconfig) {
+    SILC_LOG_ERROR(("Rehash FAILED."));
+    return FALSE;
   }
-}
 
-/* Stops the SILC server. This function is used to shutdown the server. 
-   This is usually called after the scheduler has returned. After stopping 
-   the server one should call silc_server_free. */
+  max_conns = server->config->param.connections_max;
+
+  /* Our old config is gone now. We'll unreference our reference made in
+     silc_server_init and then destroy it since we are destroying it
+     underneath the application (layer which called silc_server_init). */
+  silc_server_config_unref(&server->config_ref);
+  silc_server_config_destroy(server->config);
+
+  /* Take new config context */
+  server->config = newconfig;
+  silc_server_config_ref(&server->config_ref, server->config, server->config);
+
+  /* Reinit scheduler if necessary */
+  if (server->config->param.connections_max > max_conns)
+    silc_schedule_reinit(server->schedule, 
+                        server->config->param.connections_max);
+
+  /* Fix the server_name field */
+  if (strcmp(server->server_name, newconfig->server_info->server_name)) {
+    silc_free(server->server_name);
+    server->server_name = newconfig->server_info->server_name;
+    newconfig->server_info->server_name = NULL;
+
+    /* Update the idcache list with a fresh pointer */
+    silc_free(server->id_entry->server_name);
+    server->id_entry->server_name = strdup(server->server_name);
+    silc_idcache_del_by_context(server->local_list->servers, server->id_entry);
+    silc_idcache_add(server->local_list->servers,
+                    server->id_entry->server_name,
+                    server->id_entry->id, server->id_entry, 0, NULL);
+  }
 
-void silc_server_stop(SilcServer server)
-{
-  SILC_LOG_DEBUG(("Stopping server"));
+  silc_server_config_setlogfiles(server);
+
+  /* Change new key pair if necessary */
+  if (newconfig->server_info->public_key &&
+      !silc_pkcs_public_key_compare(server->public_key,
+                                   newconfig->server_info->public_key)) {
+    silc_pkcs_public_key_free(server->public_key);
+    silc_pkcs_private_key_free(server->private_key);
+    server->public_key = server->config->server_info->public_key;
+    server->private_key = server->config->server_info->private_key;
+    server->config->server_info->public_key = NULL;
+    server->config->server_info->private_key = NULL;
+
+    /* Allocate PKCS context for local public and private keys */
+    silc_pkcs_free(server->pkcs);
+    if (!silc_pkcs_alloc(server->public_key->name, &server->pkcs))
+      return FALSE;
+    silc_pkcs_public_key_set(server->pkcs, server->public_key);
+    silc_pkcs_private_key_set(server->pkcs, server->private_key);
+  }
+
+  /* Go through all configured routers after rehash */
+  silc_schedule_task_add(server->schedule, 0,
+                        silc_server_connect_to_router,
+                        (void *)server, 0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 
-  silc_schedule_stop(server->schedule);
-  silc_schedule_uninit(server->schedule);
+  /* Check whether our router status has changed */
+  if (server->config->servers) {
+    SilcServerConfigServer *ptr = server->config->servers;
 
-  silc_server_protocols_unregister();
+    server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       server->id_entry->server_type = SILC_BACKUP_ROUTER;
+       break;
+      }
+      ptr = ptr->next;
+    }
+  }
 
-  SILC_LOG_DEBUG(("Server stopped"));
+  SILC_LOG_DEBUG(("Server rehashed"));
+
+  return TRUE;
 }
 
-/* The heart of the server. This runs the scheduler thus runs the server. 
+/* The heart of the server. This runs the scheduler thus runs the server.
    When this returns the server has been stopped and the program will
    be terminated. */
 
 void silc_server_run(SilcServer server)
 {
-  SILC_LOG_DEBUG(("Running server"));
-
   SILC_LOG_INFO(("SILC Server started"));
 
   /* Start the scheduler, the heart of the SILC server. When this returns
@@ -468,69 +465,43 @@ void silc_server_run(SilcServer server)
   silc_schedule(server->schedule);
 }
 
-/* Timeout callback that will be called to retry connecting to remote
-   router. This is used by both normal and router server. This will wait
-   before retrying the connecting. The timeout is generated by exponential
-   backoff algorithm. */
+/* Stops the SILC server. This function is used to shutdown the server.
+   This is usually called after the scheduler has returned. After stopping
+   the server one should call silc_server_free. */
 
-SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+void silc_server_stop(SilcServer server)
 {
-  SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
-
-  SILC_LOG_INFO(("Retrying connecting to a router"));
+  SILC_LOG_DEBUG(("Stopping server"));
 
-  /* Calculate next timeout */
-  if (sconn->retry_count >= 1) {
-    sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
-    if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
-      sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
-  } else {
-    sconn->retry_timeout = server->params->retry_interval_min;
+  if (server->schedule) {
+    silc_schedule_stop(server->schedule);
+    silc_schedule_uninit(server->schedule);
+    server->schedule = NULL;
   }
-  sconn->retry_count++;
-  sconn->retry_timeout = sconn->retry_timeout +
-    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
 
-  /* If we've reached max retry count, give up. */
-  if (sconn->retry_count > server->params->retry_count && 
-      server->params->retry_keep_trying == FALSE) {
-    SILC_LOG_ERROR(("Could not connect to router, giving up"));
-    return;
-  }
+  silc_server_protocols_unregister();
 
-  /* Wait one before retrying */
-  silc_schedule_task_add(server->schedule, fd, silc_server_connect_router,
-                        context, sconn->retry_timeout, 
-                        server->params->retry_interval_min_usec,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  SILC_LOG_DEBUG(("Server stopped"));
 }
 
-/* Generic routine to use connect to a router. */
+/* Function that is called when the network connection to a router has
+   been established.  This will continue with the key exchange protocol
+   with the remote router. */
 
-SILC_TASK_CALLBACK(silc_server_connect_router)
-{    
-  SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
+void silc_server_start_key_exchange(SilcServer server,
+                                   SilcServerConnection sconn,
+                                   int sock)
+{
   SilcSocketConnection newsocket;
   SilcProtocol protocol;
   SilcServerKEInternalContext *proto_ctx;
-  int sock;
-
-  SILC_LOG_INFO(("Connecting to the router %s on port %d", 
-                sconn->remote_host, sconn->remote_port));
+  SilcServerConfigRouter *conn =
+    (SilcServerConfigRouter *) sconn->conn.ref_ptr;
+  void *context;
 
-  /* Connect to remote host */
-  sock = silc_net_create_connection(sconn->remote_port, 
-                                   sconn->remote_host);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Could not connect to router"));
-    silc_schedule_task_add(server->schedule, fd, 
-                          silc_server_connect_to_router_retry,
-                          context, 0, 1, SILC_TASK_TIMEOUT, 
-                          SILC_TASK_PRI_NORMAL);
-    return;
-  }
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
 
   /* Set socket options */
   silc_net_set_socket_nonblock(sock);
@@ -555,37 +526,137 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   proto_ctx->sock = newsocket;
   proto_ctx->rng = server->rng;
   proto_ctx->responder = FALSE;
-      
+
+  /* Set Key Exchange flags from configuration, but fall back to global
+     settings too. */
+  SILC_GET_SKE_FLAGS(conn, proto_ctx);
+  if (server->config->param.key_exchange_pfs)
+    proto_ctx->flags |= SILC_SKE_SP_FLAG_PFS;
+
   /* Perform key exchange protocol. silc_server_connect_to_router_second
      will be called after the protocol is finished. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
                      &protocol, proto_ctx,
                      silc_server_connect_to_router_second);
   newsocket->protocol = protocol;
-      
+
   /* Register a timeout task that will be executed if the protocol
      is not executed within set limit. */
-  proto_ctx->timeout_task = 
-    silc_schedule_task_add(server->schedule, sock, 
-                      silc_server_timeout_remote,
-                      server, server->params->protocol_timeout,
-                      server->params->protocol_timeout_usec,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+  proto_ctx->timeout_task =
+    silc_schedule_task_add(server->schedule, sock,
+                          silc_server_timeout_remote,
+                          server, server->config->key_exchange_timeout, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
 
   /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
-     and sets that outgoing packets may be sent to this connection as 
+     that scheduler will listen for incoming packets for this connection
+     and sets that outgoing packets may be sent to this connection as
      well. 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);
-  
+
   /* Run the protocol */
   silc_protocol_execute(protocol, server->schedule, 0, 0);
 }
-  
+
+/* Timeout callback that will be called to retry connecting to remote
+   router. This is used by both normal and router server. This will wait
+   before retrying the connecting. The timeout is generated by exponential
+   backoff algorithm. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+{
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+  SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+  SilcServerConfigConnParams *param =
+               (conn->param ? conn->param : &server->config->param);
+
+  SILC_LOG_INFO(("Retrying connecting to a router"));
+
+  /* Calculate next timeout */
+  if (sconn->retry_count >= 1) {
+    sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+    if (sconn->retry_timeout > param->reconnect_interval_max)
+      sconn->retry_timeout = param->reconnect_interval_max;
+  } else {
+    sconn->retry_timeout = param->reconnect_interval;
+  }
+  sconn->retry_count++;
+  sconn->retry_timeout = sconn->retry_timeout +
+    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
+
+  /* If we've reached max retry count, give up. */
+  if ((sconn->retry_count > param->reconnect_count) &&
+      !param->reconnect_keep_trying) {
+    SILC_LOG_ERROR(("Could not connect to router, giving up"));
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+
+  /* We will lookup a fresh pointer later */
+  silc_server_config_unref(&sconn->conn);
+
+  /* Wait one before retrying */
+  silc_schedule_task_add(server->schedule, 0, silc_server_connect_router,
+                        context, sconn->retry_timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* Generic routine to use connect to a router. */
+
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+  SilcServerConfigRouter *rconn;
+  int sock;
+
+  SILC_LOG_INFO(("Connecting to the %s %s on port %d",
+                (sconn->backup ? "backup router" : "router"),
+                sconn->remote_host, sconn->remote_port));
+
+  server->router_connect = time(NULL);
+  rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
+                                             sconn->remote_port);
+  if (!rconn) {
+    SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
+                  (sconn->backup ? "backup router" : "router"),
+                  sconn->remote_host, sconn->remote_port));
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+  silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
+
+  /* Connect to remote host */
+  sock = silc_net_create_connection(server->config->server_info->server_ip,
+                                   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)
+      silc_schedule_task_add(server->schedule, 0,
+                            silc_server_connect_to_router_retry,
+                            context, 0, 1, SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+    else
+      silc_server_config_unref(&sconn->conn);
+    return;
+  }
+
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
+}
+
 /* This function connects to our primary router or if we are a router this
    establishes all our primary routes. This is called at the start of the
    server to do authentication and key exchange with our router - called
@@ -595,71 +666,66 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
+  SilcServerConfigRouter *ptr;
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
-  /* If we are normal SILC server we need to connect to our cell's
-     router. */
   if (server->server_type == SILC_SERVER) {
     SILC_LOG_DEBUG(("We are normal server"));
+  } else if (server->server_type == SILC_ROUTER) {
+    SILC_LOG_DEBUG(("We are router"));
+  } else {
+    SILC_LOG_DEBUG(("We are backup router/normal server"));
+  }
 
-    /* Create connection to the router, if configured. */
-    if (server->config->routers) {
+  if (!server->config->routers) {
+    /* There wasn't a configured router, we will continue but we don't
+       have a connection to outside world.  We will be standalone server. */
+    SILC_LOG_DEBUG(("No router(s), server will be standalone"));
+    server->standalone = TRUE;
+    return;
+  }
+
+  /* Create the connections to all our routes */
+  for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+
+    SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+                   ptr->backup_router ? "Backup router" : "Router",
+                   ptr->initiator ? "Initiator" : "Responder",
+                   ptr->host, ptr->port));
+
+    if (ptr->initiator) {
+      /* Check whether we are connected to this host already */
+      if (silc_server_num_sockets_by_remote(server, 
+                                           silc_net_is_ip(ptr->host) ?
+                                           ptr->host : NULL,
+                                           silc_net_is_ip(ptr->host) ?
+                                           NULL : ptr->host, ptr->port,
+                                           SILC_SOCKET_TYPE_ROUTER)) {
+       SILC_LOG_DEBUG(("We are already connected to this router"));
+       continue;
+      }
 
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
-      sconn->remote_host = strdup(server->config->routers->host);
-      sconn->remote_port = server->config->routers->port;
-
-      silc_schedule_task_add(server->schedule, fd, 
-                        silc_server_connect_router,
-                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                        SILC_TASK_PRI_NORMAL);
-      return;
-    }
-  }
-
-  /* If we are a SILC router we need to establish all of our primary
-     routes. */
-  if (server->server_type == SILC_ROUTER) {
-    SilcServerConfigSectionServerConnection *ptr;
-
-    SILC_LOG_DEBUG(("We are router"));
-
-    /* Create the connections to all our routes */
-    ptr = server->config->routers;
-    while (ptr) {
-
-      SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
-                     ptr->initiator ? "Initiator" : "Responder",
-                     ptr->host, ptr->port));
-
-      if (ptr->initiator) {
-       /* 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;
-
-       silc_schedule_task_add(server->schedule, fd, 
-                          silc_server_connect_router,
-                          (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                          SILC_TASK_PRI_NORMAL);
+      sconn->remote_host = strdup(ptr->host);
+      sconn->remote_port = ptr->port;
+      sconn->backup = ptr->backup_router;
+      if (sconn->backup) {
+       sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
+       sconn->backup_replace_port = ptr->backup_replace_port;
       }
 
-      if (!ptr->next)
-       return;
+      if (!server->router_conn && !sconn->backup)
+       server->router_conn = sconn;
 
-      ptr = ptr->next;
+      silc_schedule_task_add(server->schedule, 0,
+                            silc_server_connect_router,
+                            (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
     }
   }
-
-  SILC_LOG_DEBUG(("No router(s), server will be standalone"));
-  
-  /* There wasn't a configured router, we will continue but we don't
-     have a connection to outside world.  We will be standalone server. */
-  server->standalone = TRUE;
 }
 
 /* Second part of connecting to router(s). Key exchange protocol has been
@@ -668,13 +734,13 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerKEInternalContext *ctx = 
+  SilcServerKEInternalContext *ctx =
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
   SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
-  SilcServerConfigSectionServerConnection *conn = NULL;
+  SilcServerConfigRouter *conn = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -688,20 +754,24 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
   }
-  
+
   /* We now have the key material as the result of the key exchange
      protocol. Take the key material into use. Free the raw key material
      as soon as we've set them into use. */
-  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+  if (!silc_server_protocol_ke_set_keys(server, ctx->ske,
+                                       ctx->sock, ctx->keymat,
                                        ctx->ske->prop->cipher,
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
@@ -715,15 +785,18 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
-  }    
+  }
   silc_ske_free_key_material(ctx->keymat);
 
   /* Allocate internal context for the authentication protocol. This
@@ -736,17 +809,30 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
 
-  /* Resolve the authentication method used in this connection. Check if 
+  /* Resolve the authentication method used in this connection. Check if
      we find a match from user configured connections */
-  conn = silc_server_config_find_router_conn(server->config,
-                                            sock->hostname,
-                                            sock->port);
+  if (!sconn->conn.ref_ptr)
+    conn = silc_server_config_find_router_conn(server, sock->hostname,
+                                              sock->port);
+  else
+    conn = sconn->conn.ref_ptr;
+
   if (conn) {
-    /* Match found. Use the configured authentication method */
-    proto_ctx->auth_meth = conn->auth_meth;
-    if (conn->auth_data) {
-      proto_ctx->auth_data = strdup(conn->auth_data);
-      proto_ctx->auth_data_len = strlen(conn->auth_data);
+    /* Match found. Use the configured authentication method. Take only
+       the passphrase, since for public key auth we automatically use
+       our local key pair. */
+    if (conn->passphrase) {
+      if (conn->publickeys && !server->config->prefer_passphrase_auth) {
+       proto_ctx->auth_meth = SILC_AUTH_PUBLIC_KEY;
+      } else {
+       proto_ctx->auth_data = strdup(conn->passphrase);
+       proto_ctx->auth_data_len = strlen(conn->passphrase);
+       proto_ctx->auth_meth = SILC_AUTH_PASSWORD;
+      }
+    } else if (conn->publickeys) {
+      proto_ctx->auth_meth = SILC_AUTH_PUBLIC_KEY;
+    } else {
+      proto_ctx->auth_meth = SILC_AUTH_NONE;
     }
   } else {
     SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
@@ -757,11 +843,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -778,19 +867,19 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
      but we won't start it yet. We will be receiving party of this
      protocol thus we will wait that connecting party will make
      their first move. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 
-                     &sock->protocol, proto_ctx, 
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
+                     &sock->protocol, proto_ctx,
                      silc_server_connect_to_router_final);
 
   /* Register timeout task. If the protocol is not executed inside
-     this timelimit the connection will be terminated. Currently
-     this is 15 seconds and is hard coded limit (XXX). */
-  proto_ctx->timeout_task = 
-    silc_schedule_task_add(server->schedule, sock->sock, 
-                      silc_server_timeout_remote,
-                      (void *)server, 15, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+     this timelimit the connection will be terminated. */
+  proto_ctx->timeout_task =
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_timeout_remote,
+                          (void *)server,
+                          server->config->conn_auth_timeout, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
 
   /* Run the protocol */
   silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
@@ -802,7 +891,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
 SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerConnAuthInternalContext *ctx = 
+  SilcServerConnAuthInternalContext *ctx =
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
@@ -811,27 +900,28 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcBuffer packet;
   SilcServerHBContext hb_context;
   unsigned char *id_string;
-  uint32 id_len;
+  SilcUInt32 id_len;
   SilcIDListData idata;
+  SilcServerConfigRouter *conn = NULL;
+  SilcServerConfigConnParams *param = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
   }
 
-  /* Add a task to the queue. This task receives new connections to the 
+  /* Add a task to the queue. This task receives new connections to the
      server. This task remains on the queue until the end of the program. */
-  if (!server->listenning) {
-    silc_schedule_task_add(server->schedule, server->sock, 
+  if (!server->listenning && !sconn->backup) {
+    silc_schedule_task_add(server->schedule, server->sock,
                           silc_server_accept_new_connection,
-                          (void *)server, 0, 0, 
+                          (void *)server, 0, 0,
                           SILC_TASK_FD,
                           SILC_TASK_PRI_NORMAL);
     server->listenning = TRUE;
@@ -857,15 +947,30 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   silc_buffer_free(packet);
   silc_free(id_string);
 
-  SILC_LOG_DEBUG(("Connected to router %s", sock->hostname));
+  SILC_LOG_INFO(("Connected to router %s", sock->hostname));
+
+  /* Check that we do not have this ID already */
+  id_entry = silc_idlist_find_server_by_id(server->local_list,
+                                          ctx->dest_id, TRUE, NULL);
+  if (id_entry) {
+    silc_idcache_del_by_context(server->local_list->servers, id_entry);
+  } else {
+    id_entry = silc_idlist_find_server_by_id(server->global_list,
+                                            ctx->dest_id, TRUE, NULL);
+    if (id_entry)
+      silc_idcache_del_by_context(server->global_list->servers, id_entry);
+  }
+
+  SILC_LOG_DEBUG(("New server id(%s)",
+                 silc_id_render(ctx->dest_id, SILC_ID_SERVER)));
 
-  /* Add the connected router to local server list */
-  server->standalone = FALSE;
-  id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname),
+  /* Add the connected router to global server list */
+  id_entry = silc_idlist_add_server(server->global_list,
+                                   strdup(sock->hostname),
                                    SILC_ROUTER, ctx->dest_id, NULL, sock);
   if (!id_entry) {
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
+    SILC_LOG_ERROR(("Cannot add new server entry to cache"));
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
@@ -875,73 +980,105 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   silc_free(sock->user_data);
   sock->user_data = (void *)id_entry;
   sock->type = SILC_SOCKET_TYPE_ROUTER;
-  server->id_entry->router = id_entry;
-  server->router = id_entry;
   idata = (SilcIDListData)sock->user_data;
-  idata->registered = TRUE;
+  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
+
+  conn = sconn->conn.ref_ptr;
+  param = &server->config->param;
+  if (conn && conn->param)
+    param = conn->param;
 
   /* Perform keepalive. The `hb_context' will be freed automatically
-     when finally calling the silc_socket_free function. XXX hardcoded 
-     timeout!! */
+     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, 600, hb_context,
+  silc_socket_set_heartbeat(sock, param->keepalive_secs, hb_context,
                            silc_server_perform_heartbeat,
                            server->schedule);
 
   /* Register re-key timeout */
-  idata->rekey->timeout = 3600; /* XXX hardcoded */
+  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,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(server->schedule, sock->sock,
+                        silc_server_rekey_callback,
+                        (void *)sock, idata->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
-  /* If we are router then announce our possible servers. */
-  if (server->server_type == SILC_ROUTER)
-    silc_server_announce_servers(server);
+  if (!sconn->backup) {
+    /* Mark this router our primary router if we're still standalone */
+    if (server->standalone) {
+      server->id_entry->router = id_entry;
+      server->router = id_entry;
+      server->standalone = FALSE;
+
+      /* If we are router then announce our possible servers. */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_announce_servers(server, FALSE, 0,
+                                    server->router->connection);
+
+      /* Announce our clients and channels to the router */
+      silc_server_announce_clients(server, 0, server->router->connection);
+      silc_server_announce_channels(server, 0, server->router->connection);
+    }
+  } else {
+    /* Add this server to be our backup router */
+    silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
+                          sconn->backup_replace_port, FALSE);
+  }
+
+  sock->protocol = NULL;
 
-  /* Announce our clients and channels to the router */
-  silc_server_announce_clients(server);
-  silc_server_announce_channels(server);
+  /* Call the completion callback to indicate that we've connected to
+     the router */
+  if (sconn->callback)
+    (*sconn->callback)(server, id_entry, sconn->callback_context);
 
  out:
   /* Free the temporary connection data context */
   if (sconn) {
+    silc_server_config_unref(&sconn->conn);
     silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
     silc_free(sconn);
   }
+  if (sconn == server->router_conn)
+    server->router_conn = NULL;
 
   /* Free the protocol object */
+  if (sock->protocol == protocol)
+    sock->protocol = NULL;
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  if (ctx->auth_meth == SILC_AUTH_PASSWORD)
+    silc_free(ctx->auth_data);
   silc_free(ctx);
-  sock->protocol = NULL;
 }
 
-/* Host lookup callbcak that is called after the incoming connection's
+/* Host lookup callback that is called after the incoming connection's
    IP and FQDN lookup is performed. This will actually check the acceptance
    of the incoming connection and will register the key exchange protocol
    for this connection. */
 
-static void 
+static void
 silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
                                         void *context)
 {
   SilcServer server = (SilcServer)context;
   SilcServerKEInternalContext *proto_ctx;
-  void *cconfig, *sconfig, *rconfig;
-  SilcServerConfigSectionDenyConnection *deny;
+  SilcServerConfigClient *cconfig = NULL;
+  SilcServerConfigServer *sconfig = NULL;
+  SilcServerConfigRouter *rconfig = NULL;
+  SilcServerConfigDeny *deny;
   int port;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Check whether we could resolve both IP and FQDN. */
   if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
-                   server->params->require_reverse_mapping)) {
+                   server->config->require_reverse_lookup)) {
     SILC_LOG_ERROR(("IP/DNS lookup failed %s",
                    sock->hostname ? sock->hostname :
                    sock->ip ? sock->ip : ""));
@@ -952,56 +1089,51 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   }
 
   /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
+     that scheduler will listen for incoming packets for this connection
      and sets that outgoing packets may be sent to this connection as well.
      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. */
   SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
 
-  SILC_LOG_INFO(("Incoming connection from %s (%s)", sock->hostname,
+  SILC_LOG_INFO(("Incoming connection %s (%s)", sock->hostname,
                 sock->ip));
 
   port = server->sockets[server->sock]->port; /* Listenning port */
 
   /* Check whether this connection is denied to connect to us. */
-  deny = silc_server_config_denied_conn(server->config, sock->ip, port);
+  deny = silc_server_config_find_denied(server, sock->ip);
   if (!deny)
-    deny = silc_server_config_denied_conn(server->config, sock->hostname,
-                                         port);
+    deny = silc_server_config_find_denied(server, sock->hostname);
   if (deny) {
     /* The connection is denied */
-    SILC_LOG_INFO(("Connection %s (%s) is denied", 
-                   sock->hostname, sock->ip));
-    silc_server_disconnect_remote(server, sock, deny->comment ?
-                                 deny->comment :
+    SILC_LOG_INFO(("Connection %s (%s) is denied",
+                  sock->hostname, sock->ip));
+    silc_server_disconnect_remote(server, sock, deny->reason ?
+                                 deny->reason :
                                  "Server closed connection: "
                                  "Connection refused");
     server->stat.conn_failures++;
     return;
   }
 
-  /* Check whether we have configred this sort of connection at all. We
+  /* Check whether we have configured this sort of connection at all. We
      have to check all configurations since we don't know what type of
      connection this is. */
-  if (!(cconfig = silc_server_config_find_client_conn(server->config,
-                                                     sock->ip, port)))
-    cconfig = silc_server_config_find_client_conn(server->config,
-                                                 sock->hostname, 
-                                                 port);
-  if (!(sconfig = silc_server_config_find_server_conn(server->config,
-                                                    sock->ip, 
-                                                    port)))
-    sconfig = silc_server_config_find_server_conn(server->config,
-                                                 sock->hostname,
-                                                 port);
-  if (!(rconfig = silc_server_config_find_router_conn(server->config,
-                                                    sock->ip, port)))
-    rconfig = silc_server_config_find_router_conn(server->config,
-                                                 sock->hostname, 
-                                                 port);
+  if (!(cconfig = silc_server_config_find_client(server, sock->ip)))
+    cconfig = silc_server_config_find_client(server, sock->hostname);
+  if (!(sconfig = silc_server_config_find_server_conn(server, sock->ip)))
+    sconfig = silc_server_config_find_server_conn(server, sock->hostname);
+  if (server->server_type == SILC_ROUTER) {
+    if (!(rconfig = silc_server_config_find_router_conn(server,
+                                                       sock->ip, port)))
+      rconfig = silc_server_config_find_router_conn(server, sock->hostname,
+                                                   sock->port);
+  }
   if (!cconfig && !sconfig && !rconfig) {
-    silc_server_disconnect_remote(server, sock, 
+    SILC_LOG_INFO(("Connection %s (%s) is not allowed", sock->hostname,
+                  sock->ip));
+    silc_server_disconnect_remote(server, sock,
                                  "Server closed connection: "
                                  "Connection refused");
     server->stat.conn_failures++;
@@ -1017,27 +1149,35 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   proto_ctx->sock = sock;
   proto_ctx->rng = server->rng;
   proto_ctx->responder = TRUE;
-  proto_ctx->cconfig = cconfig;
-  proto_ctx->sconfig = sconfig;
-  proto_ctx->rconfig = rconfig;
+  silc_server_config_ref(&proto_ctx->cconfig, server->config, cconfig);
+  silc_server_config_ref(&proto_ctx->sconfig, server->config, sconfig);
+  silc_server_config_ref(&proto_ctx->rconfig, server->config, rconfig);
+
+  /* Take flags for key exchange. Since we do not know what type of connection
+     this is, we go through all found configurations and use the global ones
+     as well. This will result always into strictest key exchange flags. */
+  SILC_GET_SKE_FLAGS(cconfig, proto_ctx);
+  SILC_GET_SKE_FLAGS(sconfig, proto_ctx);
+  SILC_GET_SKE_FLAGS(rconfig, proto_ctx);
+  if (server->config->param.key_exchange_pfs)
+    proto_ctx->flags |= SILC_SKE_SP_FLAG_PFS;
 
   /* Prepare the connection for key exchange protocol. We allocate the
      protocol but will not start it yet. The connector will be the
-     initiator of the protocol thus we will wait for initiation from 
+     initiator of the protocol thus we will wait for initiation from
      there before we start the protocol. */
   server->stat.auth_attempts++;
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &sock->protocol, proto_ctx, 
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
+                     &sock->protocol, proto_ctx,
                      silc_server_accept_new_connection_second);
 
   /* Register a timeout task that will be executed if the connector
-     will not start the key exchange protocol within 60 seconds. For
-     now, this is a hard coded limit. After 60 secs the connection will
-     be closed if the key exchange protocol has not been started. */
-  proto_ctx->timeout_task = 
-    silc_schedule_task_add(server->schedule, sock->sock, 
+     will not start the key exchange protocol within specified timeout
+     and the connection will be closed. */
+  proto_ctx->timeout_task =
+    silc_schedule_task_add(server->schedule, sock->sock,
                           silc_server_timeout_remote,
-                          context, 60, 0,
+                          context, server->config->key_exchange_timeout, 0,
                           SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_LOW);
 }
@@ -1062,10 +1202,11 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
     return;
   }
 
-  /* Check max connections */
-  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
+  /* Check for maximum allowed connections */
+  if (sock > server->config->param.connections_max) {
     SILC_LOG_ERROR(("Refusing connection, server is full"));
     server->stat.conn_failures++;
+    silc_net_close_connection(sock);
     return;
   }
 
@@ -1081,8 +1222,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   /* Perform asynchronous host lookup. This will lookup the IP and the
      FQDN of the remote connection. After the lookup is done the connection
      is accepted further. */
-  silc_socket_host_lookup(newsocket, TRUE, 
-                         silc_server_accept_new_connection_lookup, context, 
+  silc_socket_host_lookup(newsocket, TRUE,
+                         silc_server_accept_new_connection_lookup, context,
                          server->schedule);
 }
 
@@ -1095,7 +1236,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerKEInternalContext *ctx = 
+  SilcServerKEInternalContext *ctx =
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
@@ -1103,8 +1244,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+  if ((protocol->state == SILC_PROTOCOL_STATE_ERROR) ||
+      (protocol->state == SILC_PROTOCOL_STATE_FAILURE)) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     sock->protocol = NULL;
@@ -1113,11 +1254,13 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
+    silc_server_config_unref(&ctx->cconfig);
+    silc_server_config_unref(&ctx->sconfig);
+    silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
@@ -1127,7 +1270,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   /* We now have the key material as the result of the key exchange
      protocol. Take the key material into use. Free the raw key material
      as soon as we've set them into use. */
-  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+  if (!silc_server_protocol_ke_set_keys(server, ctx->ske,
+                                       ctx->sock, ctx->keymat,
                                        ctx->ske->prop->cipher,
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
@@ -1141,16 +1285,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
+    silc_server_config_unref(&ctx->cconfig);
+    silc_server_config_unref(&ctx->sconfig);
+    silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
     return;
-  }    
+  }
   silc_ske_free_key_material(ctx->keymat);
 
   /* Allocate internal context for the authentication protocol. This
@@ -1177,17 +1323,17 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
      but we won't start it yet. We will be receiving party of this
      protocol thus we will wait that connecting party will make
      their first move. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 
-                     &sock->protocol, proto_ctx, 
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
+                     &sock->protocol, proto_ctx,
                      silc_server_accept_new_connection_final);
 
   /* Register timeout task. If the protocol is not executed inside
-     this timelimit the connection will be terminated. Currently
-     this is 60 seconds and is hard coded limit (XXX). */
-  proto_ctx->timeout_task = 
-    silc_schedule_task_add(server->schedule, sock->sock, 
+     this timelimit the connection will be terminated. */
+  proto_ctx->timeout_task =
+    silc_schedule_task_add(server->schedule, sock->sock,
                           silc_server_timeout_remote,
-                          (void *)server, 60, 0,
+                          (void *)server,
+                          server->config->conn_auth_timeout, 0,
                           SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_LOW);
 }
@@ -1199,12 +1345,14 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
 SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerConnAuthInternalContext *ctx = 
+  SilcServerConnAuthInternalContext *ctx =
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
   SilcServerHBContext hb_context;
-  void *id_entry = NULL;
+  SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
+  void *id_entry;
+  SilcUInt32 hearbeat_timeout = server->config->param.keepalive_secs;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1217,37 +1365,48 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
+    silc_server_config_unref(&ctx->cconfig);
+    silc_server_config_unref(&ctx->sconfig);
+    silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
-    if (sock)
-      sock->protocol = NULL;
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     server->stat.auth_failures++;
     return;
   }
 
-  switch(ctx->conn_type) {
+  entry->data.last_receive = time(NULL);
+
+  switch (ctx->conn_type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry client;
+      SilcServerConfigClient *conn = ctx->cconfig.ref_ptr;
+
+      /* Verify whether this connection is after all allowed to connect */
+      if (!silc_server_connection_allowed(server, sock, ctx->conn_type,
+                                         &server->config->param,
+                                         conn->param, ctx->ske)) {
+       server->stat.auth_failures++;
+       goto out;
+      }
 
       SILC_LOG_DEBUG(("Remote host is client"));
-      SILC_LOG_INFO(("Connection from %s (%s) is client", sock->hostname,
+      SILC_LOG_INFO(("Connection %s (%s) is client", sock->hostname,
                     sock->ip));
 
       /* Add the client to the client ID cache. The nickname and Client ID
         and other information is created after we have received NEW_CLIENT
         packet from client. */
-      client = silc_idlist_add_client(server->local_list, 
-                                     NULL, NULL, NULL, NULL, NULL, sock);
+      client = silc_idlist_add_client(server->local_list,
+                                     NULL, NULL, NULL, NULL, NULL, sock, 0);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
-       silc_server_disconnect_remote(server, sock, 
+       silc_server_disconnect_remote(server, sock,
                                      "Server closed connection: "
                                      "Authentication failed");
        server->stat.auth_failures++;
@@ -1260,6 +1419,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       if (server->server_type == SILC_ROUTER)
        server->stat.cell_clients++;
 
+      /* Get connection parameters */
+      if (conn->param) {
+       if (conn->param->keepalive_secs)
+         hearbeat_timeout = conn->param->keepalive_secs;
+      }
+
       id_entry = (void *)client;
       break;
     }
@@ -1267,31 +1432,87 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry new_server;
-      SilcServerConfigSectionServerConnection *conn = 
-       ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
-       ctx->sconfig : ctx->rconfig;
+      bool initiator = FALSE;
+      bool backup_local = FALSE;
+      bool backup_router = FALSE;
+      char *backup_replace_ip = NULL;
+      SilcUInt16 backup_replace_port = 0;
+      SilcServerConfigServer *sconn = ctx->sconfig.ref_ptr;
+      SilcServerConfigRouter *rconn = ctx->rconfig.ref_ptr;
+
+      if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
+       /* Verify whether this connection is after all allowed to connect */
+       if (!silc_server_connection_allowed(server, sock, ctx->conn_type,
+                                           &server->config->param,
+                                           rconn ? rconn->param : NULL,
+                                           ctx->ske)) {
+         server->stat.auth_failures++;
+         goto out;
+       }
+
+       if (rconn) {
+         if (rconn->param) {
+           if (rconn->param->keepalive_secs)
+             hearbeat_timeout = rconn->param->keepalive_secs;
+         }
+
+         initiator = rconn->initiator;
+         backup_local = rconn->backup_local;
+         backup_router = rconn->backup_router;
+         backup_replace_ip = rconn->backup_replace_ip;
+         backup_replace_port = rconn->backup_replace_port;
+       }
+      }
+
+      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER) {
+       /* Verify whether this connection is after all allowed to connect */
+       if (!silc_server_connection_allowed(server, sock, ctx->conn_type,
+                                           &server->config->param,
+                                           sconn ? sconn->param : NULL,
+                                           ctx->ske)) {
+         server->stat.auth_failures++;
+         goto out;
+       }
+       if (sconn) {
+         if (sconn->param) {
+           if (sconn->param->keepalive_secs)
+             hearbeat_timeout = sconn->param->keepalive_secs;
+         }
 
-      SILC_LOG_DEBUG(("Remote host is %s", 
-                     ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
-                     "server" : "router"));
-      SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
-                    sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
-                    "server" : "router"));
+         backup_router = sconn->backup_router;
+       }
+      }
+
+      SILC_LOG_DEBUG(("Remote host is %s",
+                     ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                     "server" : (backup_router ?
+                                 "backup router" : "router")));
+      SILC_LOG_INFO(("Connection %s (%s) is %s", sock->hostname,
+                    sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                    "server" : (backup_router ?
+                                "backup router" : "router")));
 
       /* Add the server into server cache. The server name and Server ID
         is updated after we have received NEW_SERVER packet from the
         server. We mark ourselves as router for this server if we really
         are router. */
-      new_server = 
-       silc_idlist_add_server(server->local_list, NULL,
-                              ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
-                              SILC_SERVER : SILC_ROUTER, NULL, 
-                              ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
-                              server->id_entry : NULL, sock);
+      new_server =
+       silc_idlist_add_server((ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                               server->local_list : (backup_router ?
+                                                     server->local_list :
+                                                     server->global_list)),
+                              NULL,
+                              (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                               SILC_SERVER : SILC_ROUTER),
+                              NULL,
+                              (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                               server->id_entry : (backup_router ?
+                                                   server->id_entry : NULL)),
+                              sock);
       if (!new_server) {
        SILC_LOG_ERROR(("Could not add new server to cache"));
        silc_free(sock->user_data);
-       silc_server_disconnect_remote(server, sock, 
+       silc_server_disconnect_remote(server, sock,
                                      "Server closed connection: "
                                      "Authentication failed");
        server->stat.auth_failures++;
@@ -1307,11 +1528,23 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       id_entry = (void *)new_server;
 
+      /* If the incoming connection is router and marked as backup router
+        then add it to be one of our backups */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && backup_router) {
+       silc_server_backup_add(server, new_server, backup_replace_ip,
+                              backup_replace_port, backup_local);
+
+       /* Change it back to SERVER type since that's what it really is. */
+       if (backup_local)
+         ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
+
+       new_server->server_type = SILC_BACKUP_ROUTER;
+      }
+
       /* Check whether this connection is to be our primary router connection
-        if we dont' already have the primary route. */
+        if we do not already have the primary route. */
       if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
-       if (silc_server_config_is_primary_route(server->config) &&
-           !conn->initiator)
+       if (silc_server_config_is_primary_route(server) && !initiator)
          break;
 
        SILC_LOG_DEBUG(("We are not standalone server anymore"));
@@ -1325,15 +1558,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       break;
     }
   default:
+    goto out;
     break;
   }
 
   sock->type = ctx->conn_type;
 
   /* Add the common data structure to the ID entry. */
-  if (id_entry)
-    silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
-      
+  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
+
   /* Add to sockets internal pointer for fast referencing */
   silc_free(sock->user_data);
   sock->user_data = id_entry;
@@ -1342,24 +1575,25 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   SILC_LOG_DEBUG(("New connection authenticated"));
 
   /* Perform keepalive. The `hb_context' will be freed automatically
-     when finally calling the silc_socket_free function. XXX hardcoded 
-     timeout!! */
+     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, 600, hb_context,
+  silc_socket_set_heartbeat(sock, hearbeat_timeout, hb_context,
                            silc_server_perform_heartbeat,
                            server->schedule);
 
  out:
   silc_schedule_task_del_by_callback(server->schedule,
-                                  silc_server_failure_callback);
+                                    silc_server_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
-  if (ctx->dest_id)
-    silc_free(ctx->dest_id);
+  silc_free(ctx->dest_id);
+  silc_server_config_unref(&ctx->cconfig);
+  silc_server_config_unref(&ctx->sconfig);
+  silc_server_config_unref(&ctx->rconfig);
   silc_free(ctx);
   sock->protocol = NULL;
 }
@@ -1374,6 +1608,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   SilcIDListData idata;
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
+  SilcUInt32 sequence = 0;
   int ret;
 
   if (!sock)
@@ -1390,9 +1625,6 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
     server->stat.packets_sent++;
 
-    if (sock->outbuf->data - sock->outbuf->head)
-     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
-
     /* Send the packet */
     ret = silc_packet_send(sock, TRUE);
 
@@ -1403,17 +1635,17 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
     if (ret == -1) {
       SILC_LOG_ERROR(("Error sending packet to connection "
-                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     "%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;
     }
-    
+
     /* 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. 
+       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);
@@ -1430,42 +1662,44 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
     if (ret == -1)
       SILC_LOG_ERROR(("Error receiving packet from connection "
-                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     "%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")));
+                      "Router"), strerror(errno)));
     return;
-  }    
+  }
 
   /* EOF */
   if (ret == 0) {
     SILC_LOG_DEBUG(("Read EOF"));
-      
+
     /* If connection is disconnecting already we will finally
        close the connection */
     if (SILC_IS_DISCONNECTING(sock)) {
       if (sock->user_data)
-       silc_server_free_sock_user_data(server, sock);
+       silc_server_free_sock_user_data(server, sock, NULL);
       silc_server_close_connection(server, sock);
       return;
     }
-      
+
     SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
     SILC_SET_DISCONNECTING(sock);
 
-    /* If the closed connection was our primary router connection the
-       start re-connecting phase. */
-    if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && 
-       sock == server->router->connection)
-      silc_schedule_task_add(server->schedule, 0, 
+    if (sock->user_data) {
+      char tmp[128];
+      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,
-                            context, 1, 0,
+                            server, 1, 0,
                             SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
 
-    if (sock->user_data)
-      silc_server_free_sock_user_data(server, sock);
     silc_server_close_connection(server, sock);
     return;
   }
@@ -1473,7 +1707,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   /* If connection is disconnecting or disconnected we will ignore
      what we read. */
   if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
-    SILC_LOG_DEBUG(("Ignoring read data from disonnected connection"));
+    SILC_LOG_DEBUG(("Ignoring read data from disconnected connection"));
     return;
   }
 
@@ -1482,51 +1716,32 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   /* Get keys and stuff from ID entry */
   idata = (SilcIDListData)sock->user_data;
   if (idata) {
-    idata->last_receive = time(NULL);
     cipher = idata->receive_key;
     hmac = idata->hmac_receive;
+    sequence = idata->psn_receive;
   }
+
   /* Process the packet. This will call the parser that will then
      decrypt and parse the packet. */
-  silc_packet_receive_process(sock, cipher, hmac, silc_server_packet_parse, 
-                             server);
-}
-
-/* Callback function that the silc_packet_decrypt will call to make the
-   decision whether the packet is normal or special packet. We will 
-   return TRUE if it is normal and FALSE if it is special */
-
-static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
-                                           SilcBuffer buffer,
-                                           SilcPacketContext *packet,
-                                           void *context)
-{
-  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
-  SilcServer server = (SilcServer)parse_ctx->context;
-
-  /* Packet is normal packet, if: 
-
-     1) packet is private message packet and does not have private key set
-     2) is other packet than channel message packet
-     3) is channel message packet and remote is router and we are router 
-
-     all other packets are special packets 
-  */
-
-  if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
-      (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
-    return FALSE;
-
-  if (packet_type != SILC_PACKET_CHANNEL_MESSAGE || 
-      (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
-       parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
-       server->server_type == SILC_ROUTER))
-    return TRUE;
+  ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+                                   TRUE : FALSE, 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);
 
-  return FALSE;
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
+    silc_server_close_connection(server, sock);
+  }
 }
-  
+
 /* Parses whole packet, received earlier. */
 
 SILC_TASK_CALLBACK(silc_server_packet_parse_real)
@@ -1540,28 +1755,17 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Decrypt the received packet */
-  ret = silc_packet_decrypt(idata ? idata->receive_key : NULL, 
-                           idata ? idata->hmac_receive : NULL, 
-                           packet->buffer, packet,
-                           silc_server_packet_decrypt_check, parse_ctx);
-  if (ret < 0) {
-    SILC_LOG_WARNING(("Packet decryption failed for 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")));
-    goto out;
-  }
+  /* Parse the packet */
+  if (parse_ctx->normal)
+    ret = silc_packet_parse(packet, idata ? idata->receive_key : NULL);
+  else
+    ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
 
-  if (ret == 0) {
-    /* Parse the packet. Packet type is returned. */
-    ret = silc_packet_parse(packet);
-  } else {
-    /* Parse the packet header in special way as this is "special"
-       packet type. */
-    ret = silc_packet_parse_special(packet);
+  /* 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_NONE)
@@ -1585,12 +1789,12 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     /* Route the packet if it is not destined to us. Other ID types but
        server are handled separately after processing them. */
     if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
-       packet->dst_id_type == SILC_ID_SERVER && 
+       packet->dst_id_type == SILC_ID_SERVER &&
        sock->type != SILC_SOCKET_TYPE_CLIENT &&
-       memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
-      
+       memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
+
       /* Route the packet to fastest route for the destination ID */
-      void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
+      void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
                                packet->dst_id_type);
       if (!id)
        goto out;
@@ -1612,12 +1816,16 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
        packet->flags & SILC_PACKET_FLAG_BROADCAST &&
        !server->standalone) {
+      /* Broadcast to our primary route */
       silc_server_packet_broadcast(server, server->router->connection, packet);
+
+      /* If we have backup routers then we need to feed all broadcast
+        data to those servers. */
+      silc_server_backup_broadcast(server, sock, packet);
     }
   }
 
  out:
-  /*  silc_buffer_clear(sock->inbuf); */
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
@@ -1625,52 +1833,90 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
 /* Parser callback called by silc_packet_receive_process. This merely
    registers timeout that will handle the actual parsing when appropriate. */
 
-void silc_server_packet_parse(SilcPacketParserContext *parser_context)
+bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
+                             void *context)
 {
-  SilcServer server = (SilcServer)parser_context->context;
+  SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = parser_context->sock;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+  if (idata)
+    idata->psn_receive = parser_context->packet->sequence + 1;
+
+  /* If protocol for this connection is key exchange or rekey then we'll
+     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,
+                                 parser_context);
+
+    /* 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,
+                                 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,
+                                 silc_server_packet_parse, server);
+    return FALSE;
+  }
 
   switch (sock->type) {
   case SILC_SOCKET_TYPE_UNKNOWN:
   case SILC_SOCKET_TYPE_CLIENT:
     /* Parse the packet with timeout */
     silc_schedule_task_add(server->schedule, sock->sock,
-                      silc_server_packet_parse_real,
-                      (void *)parser_context, 0, 100000,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+                          silc_server_packet_parse_real,
+                          (void *)parser_context, 0, 100000,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_NORMAL);
     break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
-    /* Packets from servers are parsed as soon as possible */
-    silc_schedule_task_add(server->schedule, sock->sock,
-                      silc_server_packet_parse_real,
-                      (void *)parser_context, 0, 1,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+    /* Packets from servers are parsed immediately */
+    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+                                 parser_context);
     break;
   default:
-    return;
+    return TRUE;
   }
+
+  return TRUE;
 }
 
 /* Parses the packet type and calls what ever routines the packet type
    requires. This is done for all incoming packets. */
 
-void silc_server_packet_parse_type(SilcServer server, 
+void silc_server_packet_parse_type(SilcServer server,
                                   SilcSocketConnection sock,
                                   SilcPacketContext *packet)
 {
   SilcPacketType type = packet->type;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
 
   SILC_LOG_DEBUG(("Parsing packet type %d", type));
 
   /* Parse the packet type */
-  switch(type) {
+  switch (type) {
   case SILC_PACKET_DISCONNECT:
     SILC_LOG_DEBUG(("Disconnect packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
+    if (silc_string_is_ascii(packet->buffer->data, packet->buffer->len)) {
+      /* Duplicate to null terminate the string. */
+      char *message = silc_memdup(packet->buffer->data, packet->buffer->len);
+      SILC_LOG_ERROR(("%s", message));
+      silc_free(message);
+    }
     break;
 
   case SILC_PACKET_SUCCESS:
@@ -1700,7 +1946,7 @@ void silc_server_packet_parse_type(SilcServer server,
       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,
@@ -1727,7 +1973,7 @@ void silc_server_packet_parse_type(SilcServer server,
       silc_server_notify(server, sock, packet);
     break;
 
-    /* 
+    /*
      * Channel packets
      */
   case SILC_PACKET_CHANNEL_MESSAGE:
@@ -1739,6 +1985,7 @@ void silc_server_packet_parse_type(SilcServer server,
     SILC_LOG_DEBUG(("Channel Message packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
+    idata->last_receive = time(NULL);
     silc_server_channel_message(server, sock, packet);
     break;
 
@@ -1792,6 +2039,7 @@ void silc_server_packet_parse_type(SilcServer server,
     SILC_LOG_DEBUG(("Private Message packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
+    idata->last_receive = time(NULL);
     silc_server_private_message(server, sock, packet);
     break;
 
@@ -1814,8 +2062,7 @@ void silc_server_packet_parse_type(SilcServer server,
 
     if (sock->protocol && sock->protocol->protocol &&
        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
-
-      SilcServerKEInternalContext *proto_ctx = 
+      SilcServerKEInternalContext *proto_ctx =
        (SilcServerKEInternalContext *)sock->protocol->context;
 
       proto_ctx->packet = silc_packet_context_dup(packet);
@@ -1838,23 +2085,23 @@ void silc_server_packet_parse_type(SilcServer server,
         sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
       if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
-       SilcServerRekeyInternalContext *proto_ctx = 
+       SilcServerRekeyInternalContext *proto_ctx =
          (SilcServerRekeyInternalContext *)sock->protocol->context;
-       
+
        if (proto_ctx->packet)
          silc_packet_context_free(proto_ctx->packet);
-       
+
        proto_ctx->packet = silc_packet_context_dup(packet);
 
        /* Let the protocol handle the packet */
        silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
       } else {
-       SilcServerKEInternalContext *proto_ctx = 
+       SilcServerKEInternalContext *proto_ctx =
          (SilcServerKEInternalContext *)sock->protocol->context;
-       
+
        if (proto_ctx->packet)
          silc_packet_context_free(proto_ctx->packet);
-       
+
        proto_ctx->packet = silc_packet_context_dup(packet);
        proto_ctx->dest_id_type = packet->src_id_type;
        proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
@@ -1863,7 +2110,7 @@ void silc_server_packet_parse_type(SilcServer server,
          break;
 
        /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, server->schedule, 
+       silc_protocol_execute(sock->protocol, server->schedule,
                              0, 100000);
       }
     } else {
@@ -1882,23 +2129,23 @@ void silc_server_packet_parse_type(SilcServer server,
         sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
       if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
-       SilcServerRekeyInternalContext *proto_ctx = 
+       SilcServerRekeyInternalContext *proto_ctx =
          (SilcServerRekeyInternalContext *)sock->protocol->context;
-       
+
        if (proto_ctx->packet)
          silc_packet_context_free(proto_ctx->packet);
-       
+
        proto_ctx->packet = silc_packet_context_dup(packet);
 
        /* Let the protocol handle the packet */
        silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
       } else {
-       SilcServerKEInternalContext *proto_ctx = 
+       SilcServerKEInternalContext *proto_ctx =
          (SilcServerKEInternalContext *)sock->protocol->context;
-       
+
        if (proto_ctx->packet)
          silc_packet_context_free(proto_ctx->packet);
-       
+
        proto_ctx->packet = silc_packet_context_dup(packet);
        proto_ctx->dest_id_type = packet->src_id_type;
        proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
@@ -1907,7 +2154,7 @@ void silc_server_packet_parse_type(SilcServer server,
          break;
 
        /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, server->schedule, 
+       silc_protocol_execute(sock->protocol, server->schedule,
                              0, 100000);
       }
     } else {
@@ -1921,7 +2168,7 @@ void silc_server_packet_parse_type(SilcServer server,
      * Connection authentication request packet. When we receive this packet
      * we will send to the other end information about our mandatory
      * authentication method for the connection. This packet maybe received
-     * at any time. 
+     * at any time.
      */
     SILC_LOG_DEBUG(("Connection authentication request packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
@@ -1933,16 +2180,16 @@ void silc_server_packet_parse_type(SilcServer server,
      * Connection Authentication protocol packets
      */
   case SILC_PACKET_CONNECTION_AUTH:
-    /* Start of the authentication protocol. We receive here the 
+    /* Start of the authentication protocol. We receive here the
        authentication data and will verify it. */
     SILC_LOG_DEBUG(("Connection auth packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
+    if (sock->protocol && sock->protocol->protocol->type
        == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
 
-      SilcServerConnAuthInternalContext *proto_ctx = 
+      SilcServerConnAuthInternalContext *proto_ctx =
        (SilcServerConnAuthInternalContext *)sock->protocol->context;
 
       proto_ctx->packet = silc_packet_context_dup(packet);
@@ -1984,7 +2231,7 @@ void silc_server_packet_parse_type(SilcServer server,
   case SILC_PACKET_NEW_SERVER:
     /*
      * Received new server packet. This includes Server ID and some other
-     * information that we may save. This is received after server has 
+     * information that we may save. This is received after server has
      * connected to us.
      */
     SILC_LOG_DEBUG(("New Server packet"));
@@ -2046,7 +2293,7 @@ void silc_server_packet_parse_type(SilcServer server,
     if (sock->protocol && sock->protocol->protocol &&
        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
 
-      SilcServerRekeyInternalContext *proto_ctx = 
+      SilcServerRekeyInternalContext *proto_ctx =
        (SilcServerRekeyInternalContext *)sock->protocol->context;
 
       if (proto_ctx->packet)
@@ -2062,17 +2309,34 @@ void silc_server_packet_parse_type(SilcServer server,
     }
     break;
 
+  case SILC_PACKET_FTP:
+    /* FTP packet */
+    SILC_LOG_DEBUG(("FTP packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_ftp(server, sock, packet);
+    break;
+
+  case SILC_PACKET_RESUME_ROUTER:
+    /* Resume router packet received. This packet is received for backup
+       router resuming protocol. */
+    SILC_LOG_DEBUG(("Resume router packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_backup_resume_router(server, sock, packet);
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
   }
-  
+
 }
 
 /* Creates connection to a remote router. */
 
 void silc_server_create_connection(SilcServer server,
-                                  char *remote_host, uint32 port)
+                                  const char *remote_host, SilcUInt32 port)
 {
   SilcServerConnection sconn;
 
@@ -2081,11 +2345,12 @@ void silc_server_create_connection(SilcServer server,
   sconn->server = server;
   sconn->remote_host = strdup(remote_host);
   sconn->remote_port = port;
+  sconn->no_reconnect = TRUE;
 
-  silc_schedule_task_add(server->schedule, 0, 
-                    silc_server_connect_router,
-                    (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(server->schedule, 0,
+                        silc_server_connect_router,
+                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 }
 
 SILC_TASK_CALLBACK(silc_server_close_connection_final)
@@ -2098,6 +2363,9 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final)
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
+  if (!server->sockets[sock->sock])
+    return;
+
   SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
                   sock->port,
                   (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
@@ -2110,7 +2378,6 @@ void silc_server_close_connection(SilcServer server,
 
   /* Unregister all tasks */
   silc_schedule_task_del_by_fd(server->schedule, sock->sock);
-  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
 
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
@@ -2131,13 +2398,13 @@ void silc_server_close_connection(SilcServer server,
     }
   }
 
-  silc_schedule_task_add(server->schedule, 0, 
-                    silc_server_close_connection_final,
-                    (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
-                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(server->schedule, 0,
+                        silc_server_close_connection_final,
+                        (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 }
 
-/* Sends disconnect message to remote connection and disconnects the 
+/* Sends disconnect message to remote connection and disconnects the
    connection. */
 
 void silc_server_disconnect_remote(SilcServer server,
@@ -2159,8 +2426,9 @@ void silc_server_disconnect_remote(SilcServer server,
 
   /* Notify remote end that the conversation is over. The notify message
      is tried to be sent immediately. */
-  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
+  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,
                          buf, strlen(buf), TRUE);
+  silc_server_packet_queue_purge(server, sock);
 
   /* Mark the connection to be disconnected */
   SILC_SET_DISCONNECTED(sock);
@@ -2183,29 +2451,20 @@ SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
 
 /* Frees client data and notifies about client's signoff. */
 
-void silc_server_free_client_data(SilcServer server, 
+void silc_server_free_client_data(SilcServer server,
                                  SilcSocketConnection sock,
-                                 SilcClientEntry client, 
+                                 SilcClientEntry client,
                                  int notify,
-                                 char *signoff)
+                                 const char *signoff)
 {
   FreeClientInternal i = silc_calloc(1, sizeof(*i));
 
   /* If there is pending outgoing data for the client then purge it
      to the network before removing the client entry. */
-  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
-      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
-    server->stat.packets_sent++;
-
-    if (sock->outbuf->data - sock->outbuf->head)
-     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+  silc_server_packet_queue_purge(server, sock);
 
-    silc_packet_send(sock, TRUE);
-
-    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-    silc_buffer_clear(sock->outbuf);
-  }
+  if (!client->id)
+    return;
 
   /* Send SIGNOFF notify to routers. */
   if (notify && !server->standalone && server->router)
@@ -2215,433 +2474,181 @@ void silc_server_free_client_data(SilcServer server,
 
   /* Remove client from all channels */
   if (notify)
-    silc_server_remove_from_channels(server, NULL, client, 
-                                    TRUE, signoff, TRUE);
+    silc_server_remove_from_channels(server, NULL, client,
+                                    TRUE, (char *)signoff, TRUE);
   else
-    silc_server_remove_from_channels(server, NULL, client, 
+    silc_server_remove_from_channels(server, NULL, client,
                                     FALSE, NULL, FALSE);
 
-  /* 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.registered = FALSE;
-
-  /* Free the client entry and everything in it */
+  /* Update statistics */
   server->stat.my_clients--;
   server->stat.clients--;
   if (server->server_type == SILC_ROUTER)
     server->stat.cell_clients--;
-}
-
-/* Frees user_data pointer from socket connection object. This also sends
-   appropriate notify packets to the network to inform about leaving
-   entities. */
-
-void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock)
-{
-  SILC_LOG_DEBUG(("Start"));
-
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    {
-      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
-      break;
-    }
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    {
-      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
-
-      /* Free all client entries that this server owns as they will
-        become invalid now as well. */
-      if (user_data->id)
-       silc_server_remove_clients_by_server(server, user_data, TRUE);
-
-      /* If this was our primary router connection then we're lost to
-        the outside world. */
-      if (server->router == user_data) {
-       server->id_entry->router = NULL;
-       server->router = NULL;
-       server->standalone = TRUE;
-      }
-
-      /* Free the server entry */
-      silc_idlist_del_data(user_data);
-      silc_idlist_del_server(server->local_list, user_data);
-      server->stat.my_servers--;
-      server->stat.servers--;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.cell_servers--;
-      break;
-    }
-  default:
-    {
-      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
-
-      silc_idlist_del_data(user_data);
-      silc_free(user_data);
-      break;
-    }
-  }
-
-  /* If any protocol is active cancel its execution */
-  if (sock->protocol) {
-    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;
-}
-
-/* Removes the client from channels and possibly removes the channels
-   as well.  After removing those channels that exist, their channel
-   keys are regnerated. This is called only by the function
-   silc_server_remove_clients_by_server. */
-
-static void silc_server_remove_clients_channels(SilcServer server, 
-                                               SilcSocketConnection sock,
-                                               SilcClientEntry client,
-                                               SilcHashTable channels)
-{
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-  SilcBuffer clidp;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!client || !client->id)
-    return;
-
-  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
-  /* Remove the client from all channels. The client is removed from
-     the channels' user list. */
-  silc_hash_table_list(client->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    channel = chl->channel;
-
-    /* Remove channel from client's channel list */
-    silc_hash_table_del(client->channels, channel);
-
-    /* Remove channel if there is no users anymore */
-    if (server->server_type == SILC_ROUTER &&
-       silc_hash_table_count(channel->user_list) < 2) {
-
-      if (silc_hash_table_find(channels, channel, NULL, NULL))
-       silc_hash_table_del(channels, channel);
-
-      if (channel->rekey)
-       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
-      continue;
-    }
-
-    /* Remove client from channel's client list */
-    silc_hash_table_del(channel->user_list, chl->client);
-    silc_free(chl);
-    server->stat.my_chanclients--;
-
-    /* If there is no global users on the channel anymore mark the channel
-       as local channel. Do not check if the removed client is local client. */
-    if (server->server_type == SILC_SERVER && channel->global_users && 
-       chl->client->router && !silc_server_channel_has_global(channel))
-      channel->global_users = FALSE;
-
-    /* If there is not at least one local user on the channel then we don't
-       need the channel entry anymore, we can remove it safely. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_local(channel)) {
-
-      if (silc_hash_table_find(channels, channel, NULL, NULL))
-       silc_hash_table_del(channels, channel);
-
-      if (channel->rekey)
-       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
-      if (channel->founder_key) {
-       /* The founder auth data exists, do not remove the channel entry */
-       SilcChannelClientEntry chl2;
-       SilcHashTableList htl2;
-
-       channel->id = NULL;
-
-       silc_hash_table_list(channel->user_list, &htl2);
-       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
-         silc_hash_table_del(chl2->client->channels, channel);
-         silc_hash_table_del(channel->user_list, chl2->client);
-         silc_free(chl2);
-       }
-       continue;
-      }
-
-      /* Remove the channel entry */
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
-      continue;
-    }
-
-    /* Add the channel to the the channels list to regenerate the 
-       channel key */
-    if (!silc_hash_table_find(channels, channel, NULL, NULL))
-      silc_hash_table_add(channels, channel, channel);
-  }
+  SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+  SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 
-  silc_buffer_free(clidp);
+  /* 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->router = NULL;
+  client->connection = NULL;
+  client->mode = 0;
 }
 
-/* This function is used to remove all client entries by the server `entry'.
-   This is called when the connection is lost to the server. In this case
-   we must invalidate all the client entries owned by the server `entry'. 
-   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
-   distributed to our local clients. */
+/* Frees user_data pointer from socket connection object. This also sends
+   appropriate notify packets to the network to inform about leaving
+   entities. */
 
-int silc_server_remove_clients_by_server(SilcServer server, 
-                                        SilcServerEntry entry,
-                                        int server_signoff)
+void silc_server_free_sock_user_data(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    const char *signoff_message)
 {
-  SilcIDCacheList list = NULL;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcClientEntry client = NULL;
-  SilcBuffer idp;
-  SilcClientEntry *clients = NULL;
-  uint32 clients_c = 0;
-  unsigned char **argv = NULL;
-  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
-  SilcHashTableList htl;
-  SilcChannelEntry channel;
-  SilcHashTable channels;
-  int i;
-
   SILC_LOG_DEBUG(("Start"));
 
-  /* Allocate the hash table that holds the channels that require
-     channel key re-generation after we've removed this server's clients
-     from the channels. */
-  channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
-                                  NULL, NULL, TRUE);
-
-  if (server_signoff) {
-    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
-    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
-    argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-    memcpy(argv[argc], idp->data, idp->len);
-    argv_lens[argc] = idp->len;
-    argv_types[argc] = argc + 1;
-    argc++;
-    silc_buffer_free(idp);
-  }
-
-  if (silc_idcache_get_all(server->local_list->clients, &list)) {
+  switch (sock->type) {
+  case SILC_SOCKET_TYPE_CLIENT:
+    {
+      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
+      silc_server_free_client_data(server, sock, user_data, TRUE,
+                                  signoff_message);
+      break;
+    }
+  case SILC_SOCKET_TYPE_SERVER:
+  case SILC_SOCKET_TYPE_ROUTER:
+    {
+      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+      SilcServerEntry backup_router = NULL;
 
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (client->data.registered == FALSE) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
+      if (user_data->id)
+       backup_router = silc_server_backup_get(server, user_data->id);
 
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
+      /* 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);
+
+         server->id_entry->router = NULL;
+         server->router = NULL;
+         server->standalone = TRUE;
+         backup_router = NULL;
+       } else {
+         SILC_LOG_INFO(("New primary router is backup router %s",
+                        backup_router->server_name));
+         SILC_LOG_DEBUG(("New primary router is backup router %s",
+                         backup_router->server_name));
+         server->id_entry->router = backup_router;
+         server->router = backup_router;
+         server->router_connect = time(0);
+         server->backup_primary = TRUE;
+         if (server->server_type == SILC_BACKUP_ROUTER) {
+           server->server_type = SILC_ROUTER;
+
+           /* We'll need to constantly try to reconnect to the primary
+              router so that we'll see when it comes back online. */
+           silc_server_backup_reconnect(server, sock->ip, sock->port,
+                                        silc_server_backup_connected,
+                                        NULL);
          }
 
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
+         /* Mark this connection as replaced */
+         silc_server_backup_replaced_add(server, user_data->id,
+                                         backup_router);
        }
-
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->local_list, client);
-
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
+      } 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);
       }
-    }
-    silc_idcache_list_free(list);
-  }
-  
-  if (silc_idcache_get_all(server->global_list->clients, &list)) {
-
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (client->data.registered == FALSE) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-       
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
-         }
-
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
 
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
-       }
+      if (!backup_router) {
+       /* Free all client entries that this server owns as they will
+          become invalid now as well. */
+       if (user_data->id)
+         silc_server_remove_clients_by_server(server, user_data, TRUE);
+       if (server->server_type == SILC_SERVER)
+         silc_server_remove_channels_by_server(server, user_data);
+      } else {
+       /* Update the client entries of this server to the new backup
+          router. This also removes the clients that *really* was owned
+          by the primary router and went down with the router.  */
+       silc_server_update_clients_by_server(server, user_data, backup_router,
+                                            TRUE, TRUE);
+       silc_server_update_servers_by_server(server, user_data, backup_router);
+       if (server->server_type == SILC_SERVER)
+         silc_server_update_channels_by_server(server, user_data,
+                                               backup_router);
+      }
 
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->global_list, client);
+      /* Free the server entry */
+      silc_server_backup_del(server, user_data);
+      silc_server_backup_replaced_del(server, user_data);
+      silc_idlist_del_data(user_data);
+      if (!silc_idlist_del_server(server->local_list, user_data))
+       silc_idlist_del_server(server->global_list, user_data);
+      server->stat.my_servers--;
+      server->stat.servers--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_servers--;
 
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
+      if (backup_router) {
+       /* Announce all of our stuff that was created about 5 minutes ago.
+          The backup router knows all the other stuff already. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server, FALSE, time(0) - 300,
+                                      backup_router->connection);
+
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, time(0) - 300,
+                                    backup_router->connection);
+       silc_server_announce_channels(server, time(0) - 300,
+                                     backup_router->connection);
       }
+      break;
     }
-    silc_idcache_list_free(list);
-  }
-
-  /* Send the SERVER_SIGNOFF notify */
-  if (server_signoff) {
-    SilcBuffer args;
-
-    /* Send SERVER_SIGNOFF notify to our primary router */
-    if (!server->standalone && server->router &&
-       server->router != entry) {
-      args = silc_argument_payload_encode(1, argv, argv_lens,
-                                         argv_types);
-      silc_server_send_notify_args(server, 
-                                  server->router->connection,
-                                  server->server_type == SILC_SERVER ? 
-                                  FALSE : TRUE, 
-                                  SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
-      silc_buffer_free(args);
-    }
+  default:
+    {
+      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
 
-    args = silc_argument_payload_encode(argc, argv, argv_lens,
-                                       argv_types);
-    /* Send to local clients */
-    for (i = 0; i < clients_c; i++) {
-      silc_server_send_notify_args(server, clients[i]->connection,
-                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
+      silc_idlist_del_data(user_data);
+      silc_free(user_data);
+      break;
     }
-
-    silc_free(clients);
-    silc_buffer_free(args);
-    for (i = 0; i < argc; i++)
-      silc_free(argv[i]);
-    silc_free(argv);
-    silc_free(argv_lens);
-    silc_free(argv_types);
-  }
-
-  /* We must now re-generate the channel key for all channels that had
-     this server's client(s) on the channel. As they left the channel we
-     must re-generate the channel key. */
-  silc_hash_table_list(channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
-    if (!silc_server_create_channel_key(server, channel, 0))
-      return FALSE;
-    silc_server_send_channel_key(server, NULL, channel, 
-                                server->server_type == SILC_ROUTER ? 
-                                FALSE : !server->standalone);
-  }
-  silc_hash_table_free(channels);
-
-  return TRUE;
-}
-
-/* Checks whether given channel has global users.  If it does this returns
-   TRUE and FALSE if there is only locally connected clients on the channel. */
-
-int silc_server_channel_has_global(SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-
-  silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (chl->client->router)
-      return TRUE;
   }
 
-  return FALSE;
-}
-
-/* Checks whether given channel has locally connected users.  If it does this
-   returns TRUE and FALSE if there is not one locally connected client. */
-
-int silc_server_channel_has_local(SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-
-  silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (!chl->client->router)
-      return TRUE;
+  /* If any protocol is active cancel its execution */
+  if (sock->protocol) {
+    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 FALSE;
+  sock->user_data = NULL;
 }
 
 /* Removes client from all channels it has joined. This is used when client
    connection is disconnected. If the client on a channel is last, the
    channel is removed as well. This sends the SIGNOFF notify types. */
 
-void silc_server_remove_from_channels(SilcServer server, 
+void silc_server_remove_from_channels(SilcServer server,
                                      SilcSocketConnection sock,
                                      SilcClientEntry client,
                                      int notify,
@@ -2674,31 +2681,34 @@ void silc_server_remove_from_channels(SilcServer server,
        silc_hash_table_count(channel->user_list) < 2) {
       if (channel->rekey)
        silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else
+        silc_idlist_del_channel(server->global_list, channel);
       continue;
     }
 
     /* Remove client from channel's client list */
     silc_hash_table_del(channel->user_list, chl->client);
-    silc_free(chl);
-    server->stat.my_chanclients--;
+    channel->user_count--;
 
     /* If there is no global users on the channel anymore mark the channel
        as local channel. Do not check if the removed client is local client. */
-    if (server->server_type == SILC_SERVER && channel->global_users && 
+    if (server->server_type != SILC_ROUTER && channel->global_users &&
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
+    silc_free(chl);
+    server->stat.my_chanclients--;
+
     /* If there is not at least one local user on the channel then we don't
        need the channel entry anymore, we can remove it safely. */
-    if (server->server_type == SILC_SERVER &&
+    if (server->server_type != SILC_ROUTER &&
        !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_NOTIFY_TYPE_SIGNOFF, 
+                                          SILC_NOTIFY_TYPE_SIGNOFF,
                                           signoff_message ? 2 : 1,
                                           clidp->data, clidp->len,
                                           signoff_message, signoff_message ?
@@ -2712,21 +2722,24 @@ void silc_server_remove_from_channels(SilcServer server,
        SilcChannelClientEntry chl2;
        SilcHashTableList htl2;
 
-       channel->id = NULL;
+       channel->disabled = TRUE;
 
        silc_hash_table_list(channel->user_list, &htl2);
        while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
          silc_hash_table_del(chl2->client->channels, channel);
          silc_hash_table_del(channel->user_list, chl2->client);
+         channel->user_count--;
          silc_free(chl2);
        }
+       silc_hash_table_list_reset(&htl2);
        continue;
       }
 
       /* Remove the channel entry */
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else
+        silc_idlist_del_channel(server->global_list, channel);
       continue;
     }
 
@@ -2734,7 +2747,7 @@ void silc_server_remove_from_channels(SilcServer server,
        the entire channel. */
     if (notify)
       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                        SILC_NOTIFY_TYPE_SIGNOFF, 
+                                        SILC_NOTIFY_TYPE_SIGNOFF,
                                         signoff_message ? 2 : 1,
                                         clidp->data, clidp->len,
                                         signoff_message, signoff_message ?
@@ -2743,16 +2756,18 @@ void silc_server_remove_from_channels(SilcServer server,
     if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
       /* Re-generate channel key */
       if (!silc_server_create_channel_key(server, channel, 0))
-       return;
-      
+       goto out;
+
       /* Send the channel key to the channel. The key of course is not sent
         to the client who was removed from the channel. */
-      silc_server_send_channel_key(server, client->connection, channel, 
-                                  server->server_type == SILC_ROUTER ? 
+      silc_server_send_channel_key(server, client->connection, channel,
+                                  server->server_type == SILC_ROUTER ?
                                   FALSE : !server->standalone);
     }
   }
 
+ out:
+  silc_hash_table_list_reset(&htl);
   silc_buffer_free(clidp);
 }
 
@@ -2762,7 +2777,7 @@ void silc_server_remove_from_channels(SilcServer server,
    last client leaves the channel. If `notify' is FALSE notify messages
    are not sent. */
 
-int silc_server_remove_from_one_channel(SilcServer server, 
+int silc_server_remove_from_one_channel(SilcServer server,
                                        SilcSocketConnection sock,
                                        SilcChannelEntry channel,
                                        SilcClientEntry client,
@@ -2791,36 +2806,39 @@ int silc_server_remove_from_one_channel(SilcServer server,
       silc_hash_table_count(channel->user_list) < 2) {
     if (channel->rekey)
       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-    if (!silc_idlist_del_channel(server->local_list, channel))
+    if (silc_idlist_del_channel(server->local_list, channel))
+      server->stat.my_channels--;
+    else
       silc_idlist_del_channel(server->global_list, channel);
     silc_buffer_free(clidp);
-    server->stat.my_channels--;
     return FALSE;
   }
 
   /* Remove client from channel's client list */
   silc_hash_table_del(channel->user_list, chl->client);
-  silc_free(chl);
-  server->stat.my_chanclients--;
-  
+  channel->user_count--;
+
   /* If there is no global users on the channel anymore mark the channel
      as local channel. Do not check if the client is local client. */
-  if (server->server_type == SILC_SERVER && channel->global_users &&
+  if (server->server_type != SILC_ROUTER && channel->global_users &&
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
+  silc_free(chl);
+  server->stat.my_chanclients--;
+
   /* If there is not at least one local user on the channel then we don't
      need the channel entry anymore, we can remove it safely. */
-  if (server->server_type == SILC_SERVER &&
+  if (server->server_type != SILC_ROUTER &&
       !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_NOTIFY_TYPE_LEAVE, 1,
                                         clidp->data, clidp->len);
-    
+
     silc_buffer_free(clidp);
-    
+
     if (channel->rekey)
       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 
@@ -2828,22 +2846,25 @@ int silc_server_remove_from_one_channel(SilcServer server,
       /* The founder auth data exists, do not remove the channel entry */
       SilcChannelClientEntry chl2;
       SilcHashTableList htl2;
-      
-      channel->id = NULL;
-      
+
+      channel->disabled = TRUE;
+
       silc_hash_table_list(channel->user_list, &htl2);
       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
        silc_hash_table_del(chl2->client->channels, channel);
        silc_hash_table_del(channel->user_list, chl2->client);
+       channel->user_count--;
        silc_free(chl2);
       }
+      silc_hash_table_list_reset(&htl2);
       return FALSE;
     }
 
     /* Remove the channel entry */
-    if (!silc_idlist_del_channel(server->local_list, channel))
+    if (silc_idlist_del_channel(server->local_list, channel))
+      server->stat.my_channels--;
+    else
       silc_idlist_del_channel(server->global_list, channel);
-    server->stat.my_channels--;
     return FALSE;
   }
 
@@ -2857,25 +2878,8 @@ int silc_server_remove_from_one_channel(SilcServer server,
   return TRUE;
 }
 
-/* Returns TRUE if the given client is on the channel.  FALSE if not. 
-   This works because we assure that the user list on the channel is
-   always in up to date thus we can only check the channel list from 
-   `client' which is faster than checking the user list from `channel'. */
-
-int silc_server_client_on_channel(SilcClientEntry client,
-                                 SilcChannelEntry channel)
-{
-  if (!client || !channel)
-    return FALSE;
-
-  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
-    return TRUE;
-
-  return FALSE;
-}
-
 /* Timeout callback. This is called if connection is idle or for some
-   other reason is not responding within some period of time. This 
+   other reason is not responding within some period of time. This
    disconnects the remote end. */
 
 SILC_TASK_CALLBACK(silc_server_timeout_remote)
@@ -2888,16 +2892,21 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
   if (!sock)
     return;
 
+  SILC_LOG_ERROR(("No response from %s (%s), Connection timeout",
+                 sock->hostname, sock->ip));
+
   /* 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) {
+    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 (sock->user_data)
-    silc_server_free_sock_user_data(server, sock);
+    silc_server_free_sock_user_data(server, sock, NULL);
 
   silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                "Connection timeout");
@@ -2908,9 +2917,9 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
    are created by routers thus this function is never used by normal
    server. */
 
-SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
+SilcChannelEntry silc_server_create_new_channel(SilcServer server,
                                                SilcServerID *router_id,
-                                               char *cipher, 
+                                               char *cipher,
                                                char *hmac,
                                                char *channel_name,
                                                int broadcast)
@@ -2923,9 +2932,9 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   SILC_LOG_DEBUG(("Creating new channel"));
 
   if (!cipher)
-    cipher = "aes-256-cbc";
+    cipher = SILC_DEFAULT_CIPHER;
   if (!hmac)
-    hmac = "hmac-sha1-96";
+    hmac = SILC_DEFAULT_HMAC;
 
   /* Allocate cipher */
   if (!silc_cipher_alloc(cipher, &key))
@@ -2939,15 +2948,24 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 
   channel_name = strdup(channel_name);
 
+  /* Create the channel ID */
+  if (!silc_id_create_channel_id(server, router_id, server->rng,
+                                &channel_id)) {
+    silc_free(channel_name);
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
+    return NULL;
+  }
+
   /* Create the channel */
-  silc_id_create_channel_id(router_id, server->rng, &channel_id);
-  entry = silc_idlist_add_channel(server->local_list, channel_name, 
-                                 SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key, newhmac);
+  entry = silc_idlist_add_channel(server->local_list, channel_name,
+                                 SILC_CHANNEL_MODE_NONE, channel_id,
+                                 NULL, key, newhmac, 0);
   if (!entry) {
     silc_free(channel_name);
     silc_cipher_free(key);
     silc_hmac_free(newhmac);
+    silc_free(channel_id);
     return NULL;
   }
 
@@ -2955,21 +2973,17 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   entry->hmac_name = strdup(hmac);
 
   /* Now create the actual key material */
-  if (!silc_server_create_channel_key(server, entry, 
+  if (!silc_server_create_channel_key(server, entry,
                                      silc_cipher_get_key_len(key) / 8)) {
-    silc_free(channel_name);
-    silc_cipher_free(key);
-    silc_hmac_free(newhmac);
-    silc_free(entry->cipher);
-    silc_free(entry->hmac_name);
+    silc_idlist_del_channel(server->local_list, entry);
     return NULL;
   }
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
   if (broadcast && server->standalone == FALSE)
-    silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, 
+    silc_server_send_new_channel(server, server->router->connection, TRUE,
+                                channel_name, entry->id,
                                 silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
@@ -2980,9 +2994,9 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 
 /* Same as above but creates the channel with Channel ID `channel_id. */
 
-SilcChannelEntry 
-silc_server_create_new_channel_with_id(SilcServer server, 
-                                      char *cipher, 
+SilcChannelEntry
+silc_server_create_new_channel_with_id(SilcServer server,
+                                      char *cipher,
                                       char *hmac,
                                       char *channel_name,
                                       SilcChannelID *channel_id,
@@ -2995,9 +3009,9 @@ silc_server_create_new_channel_with_id(SilcServer server,
   SILC_LOG_DEBUG(("Creating new channel"));
 
   if (!cipher)
-    cipher = "aes-256-cbc";
+    cipher = SILC_DEFAULT_CIPHER;
   if (!hmac)
-    hmac = "hmac-sha1-96";
+    hmac = SILC_DEFAULT_HMAC;
 
   /* Allocate cipher */
   if (!silc_cipher_alloc(cipher, &key))
@@ -3012,26 +3026,28 @@ silc_server_create_new_channel_with_id(SilcServer server,
   channel_name = strdup(channel_name);
 
   /* Create the channel */
-  entry = silc_idlist_add_channel(server->local_list, channel_name, 
-                                 SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key, newhmac);
+  entry = silc_idlist_add_channel(server->local_list, channel_name,
+                                 SILC_CHANNEL_MODE_NONE, channel_id,
+                                 NULL, key, newhmac, 0);
   if (!entry) {
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
     silc_free(channel_name);
     return NULL;
   }
 
   /* Now create the actual key material */
-  if (!silc_server_create_channel_key(server, entry, 
+  if (!silc_server_create_channel_key(server, entry,
                                      silc_cipher_get_key_len(key) / 8)) {
-    silc_free(channel_name);
+    silc_idlist_del_channel(server->local_list, entry);
     return NULL;
   }
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
   if (broadcast && server->standalone == FALSE)
-    silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, 
+    silc_server_send_new_channel(server, server->router->connection, TRUE,
+                                channel_name, entry->id,
                                 silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
@@ -3047,28 +3063,25 @@ SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
   SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
   SilcServer server = (SilcServer)rekey->context;
 
+  rekey->task = NULL;
+
   if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len))
     return;
-  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
 
-  silc_schedule_task_add(server->schedule, 0, 
-                    silc_server_channel_key_rekey,
-                    (void *)rekey, 3600, 0,
-                    SILC_TASK_TIMEOUT,
-                    SILC_TASK_PRI_NORMAL);
+  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
 }
 
 /* Generates new channel key. This is used to create the initial channel key
    but also to re-generate new key for channel. If `key_len' is provided
    it is the bytes of the key length. */
 
-bool silc_server_create_channel_key(SilcServer server, 
+bool silc_server_create_channel_key(SilcServer server,
                                    SilcChannelEntry channel,
-                                   uint32 key_len)
+                                   SilcUInt32 key_len)
 {
   int i;
   unsigned char channel_key[32], hash[32];
-  uint32 len;
+  SilcUInt32 len;
 
   SILC_LOG_DEBUG(("Generating channel key"));
 
@@ -3078,8 +3091,10 @@ bool silc_server_create_channel_key(SilcServer server,
   }
 
   if (!channel->channel_key)
-    if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
+    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key)) {
+      channel->channel_key = NULL;
       return FALSE;
+    }
 
   if (key_len)
     len = key_len;
@@ -3090,7 +3105,7 @@ bool silc_server_create_channel_key(SilcServer server,
 
   /* Create channel key */
   for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
-  
+
   /* Set the key */
   silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
 
@@ -3102,15 +3117,15 @@ bool silc_server_create_channel_key(SilcServer server,
 
   /* Save the key */
   channel->key_len = len * 8;
-  channel->key = silc_calloc(len, sizeof(*channel->key));
-  memcpy(channel->key, channel_key, len);
+  channel->key = silc_memdup(channel_key, len);
   memset(channel_key, 0, sizeof(channel_key));
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
-  silc_hash_make(channel->hmac->hash, channel->key, len, hash);
-  silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+  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)));
   memset(hash, 0, sizeof(hash));
 
   if (server->server_type == SILC_ROUTER) {
@@ -3119,23 +3134,22 @@ bool silc_server_create_channel_key(SilcServer server,
     channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
     channel->rekey->key_len = key_len;
-
-#if 0
-    /* XXX Now this cannot be a good thing */
-    silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_channel_key_rekey);
-#endif
-    silc_schedule_task_add(server->schedule, 0, 
-                      silc_server_channel_key_rekey,
-                      (void *)channel->rekey, 3600, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+    if (channel->rekey->task)
+      silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+    channel->rekey->task =
+      silc_schedule_task_add(server->schedule, 0,
+                            silc_server_channel_key_rekey,
+                            (void *)channel->rekey,
+                            server->config->channel_rekey_secs, 0,
+                            SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
   }
 
   return TRUE;
 }
 
-/* Saves the channel key found in the encoded `key_payload' buffer. This 
+/* Saves the channel key found in the encoded `key_payload' buffer. This
    function is used when we receive Channel Key Payload and also when we're
    processing JOIN command reply. Returns entry to the channel. */
 
@@ -3146,15 +3160,16 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
   unsigned char *tmp, hash[32];
-  uint32 tmp_len;
+  SilcUInt32 tmp_len;
   char *cipher;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Decode channel key payload */
-  payload = silc_channel_key_payload_parse(key_payload);
+  payload = silc_channel_key_payload_parse(key_payload->data,
+                                          key_payload->len);
   if (!payload) {
-    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    SILC_LOG_ERROR(("Bad channel key payload received, dropped"));
     channel = NULL;
     goto out;
   }
@@ -3174,7 +3189,8 @@ 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"));
+       SILC_LOG_ERROR(("Received key for non-existent channel %s",
+                       silc_id_render(id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -3201,6 +3217,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   /* Create new cipher */
   if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel->channel_key = NULL;
     channel = NULL;
     goto out;
   }
@@ -3211,15 +3228,15 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   /* Save the key */
   channel->key_len = tmp_len * 8;
-  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
-  memcpy(channel->key, tmp, tmp_len);
+  channel->key = silc_memdup(tmp, tmp_len);
   silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
-  silc_hash_make(channel->hmac->hash, tmp, tmp_len, hash);
-  silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+  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)));
 
   memset(hash, 0, sizeof(hash));
   memset(tmp, 0, tmp_len);
@@ -3229,19 +3246,20 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
     channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
-
-    silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_channel_key_rekey);
-    silc_schedule_task_add(server->schedule, 0, 
-                      silc_server_channel_key_rekey,
-                      (void *)channel->rekey, 3600, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+    if (channel->rekey->task)
+      silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+    channel->rekey->task =
+      silc_schedule_task_add(server->schedule, 0,
+                            silc_server_channel_key_rekey,
+                            (void *)channel->rekey,
+                            server->config->channel_rekey_secs, 0,
+                            SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
   }
 
  out:
-  if (id)
-    silc_free(id);
+  silc_free(id);
   if (payload)
     silc_channel_key_payload_free(payload);
 
@@ -3257,8 +3275,7 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock,
 {
   SilcServerHBContext hb = (SilcServerHBContext)hb_context;
 
-  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
-                 sock->ip));
+  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
 
   /* Send the heartbeat */
   silc_server_send_heartbeat(hb->server, sock);
@@ -3268,8 +3285,10 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock,
    form is dictated by the New ID payload. */
 
 static void silc_server_announce_get_servers(SilcServer server,
+                                            SilcServerEntry remote,
                                             SilcIDList id_list,
-                                            SilcBuffer *servers)
+                                            SilcBuffer *servers,
+                                            unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3282,11 +3301,21 @@ static void silc_server_announce_get_servers(SilcServer server,
       while (id_cache) {
        entry = (SilcServerEntry)id_cache->context;
 
+       /* Do not announce the one we've sending our announcements and
+          do not announce ourself. Also check the creation time if it's
+          provided. */
+       if ((entry == remote) || (entry == server->id_entry) ||
+           (creation_time && entry->data.created < creation_time)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
+
        idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
 
-       *servers = silc_buffer_realloc(*servers, 
-                                      (*servers ? 
-                                       (*servers)->truelen + idp->len : 
+       *servers = silc_buffer_realloc(*servers,
+                                      (*servers ?
+                                       (*servers)->truelen + idp->len :
                                        idp->len));
        silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
        silc_buffer_put(*servers, idp->data, idp->len);
@@ -3302,27 +3331,49 @@ static void silc_server_announce_get_servers(SilcServer server,
   }
 }
 
+static SilcBuffer
+silc_server_announce_encode_notify(SilcNotifyType notify, SilcUInt32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer p;
+
+  va_start(ap, argc);
+  p = silc_notify_payload_encode(notify, argc, ap);
+  va_end(ap);
+
+  return p;
+}
+
 /* This function is used by router to announce existing servers to our
-   primary router when we've connected to it. */
+   primary router when we've connected to it. If `creation_time' is non-zero
+   then only the servers that has been created after the `creation_time'
+   will be announced. */
 
-void silc_server_announce_servers(SilcServer server)
+void silc_server_announce_servers(SilcServer server, bool global,
+                                 unsigned long creation_time,
+                                 SilcSocketConnection remote)
 {
   SilcBuffer servers = NULL;
 
   SILC_LOG_DEBUG(("Announcing servers"));
 
   /* Get servers in local list */
-  silc_server_announce_get_servers(server, server->local_list, &servers);
+  silc_server_announce_get_servers(server, remote->user_data,
+                                  server->local_list, &servers,
+                                  creation_time);
 
-  /* Get servers in global list */
-  silc_server_announce_get_servers(server, server->global_list, &servers);
+  if (global)
+    /* Get servers in global list */
+    silc_server_announce_get_servers(server, remote->user_data,
+                                    server->global_list, &servers,
+                                    creation_time);
 
   if (servers) {
     silc_buffer_push(servers, servers->data - servers->head);
     SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
                            servers->data, servers->len, TRUE);
 
@@ -3335,12 +3386,16 @@ void silc_server_announce_servers(SilcServer server)
 
 static void silc_server_announce_get_clients(SilcServer server,
                                             SilcIDList id_list,
-                                            SilcBuffer *clients)
+                                            SilcBuffer *clients,
+                                            SilcBuffer *umodes,
+                                            unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
   SilcClientEntry client;
   SilcBuffer idp;
+  SilcBuffer tmp;
+  unsigned char mode[4];
 
   /* Go through all clients in the list */
   if (silc_idcache_get_all(id_list->clients, &list)) {
@@ -3348,15 +3403,35 @@ static void silc_server_announce_get_clients(SilcServer server,
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
 
+       if (creation_time && client->data.created < creation_time) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
+
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-       *clients = silc_buffer_realloc(*clients, 
-                                      (*clients ? 
-                                       (*clients)->truelen + idp->len : 
+       *clients = silc_buffer_realloc(*clients,
+                                      (*clients ?
+                                       (*clients)->truelen + idp->len :
                                        idp->len));
        silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
        silc_buffer_put(*clients, idp->data, idp->len);
        silc_buffer_pull(*clients, idp->len);
+
+       SILC_PUT32_MSB(client->mode, mode);
+       tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+                                                2, idp->data, idp->len,
+                                                mode, 4);
+       *umodes = silc_buffer_realloc(*umodes,
+                                     (*umodes ?
+                                      (*umodes)->truelen + tmp->len :
+                                      tmp->len));
+       silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+       silc_buffer_put(*umodes, tmp->data, tmp->len);
+       silc_buffer_pull(*umodes, tmp->len);
+       silc_buffer_free(tmp);
+
        silc_buffer_free(idp);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -3369,47 +3444,69 @@ static void silc_server_announce_get_clients(SilcServer server,
 }
 
 /* This function is used to announce our existing clients to our router
-   when we've connected to it. */
+   when we've connected to it. If `creation_time' is non-zero then only
+   the clients that has been created after the `creation_time' will be
+   announced. */
 
-void silc_server_announce_clients(SilcServer server)
+void silc_server_announce_clients(SilcServer server,
+                                 unsigned long creation_time,
+                                 SilcSocketConnection remote)
 {
   SilcBuffer clients = NULL;
+  SilcBuffer umodes = NULL;
 
   SILC_LOG_DEBUG(("Announcing clients"));
 
   /* Get clients in local list */
   silc_server_announce_get_clients(server, server->local_list,
-                                  &clients);
+                                  &clients, &umodes, creation_time);
 
   /* As router we announce our global list as well */
   if (server->server_type == SILC_ROUTER)
     silc_server_announce_get_clients(server, server->global_list,
-                                    &clients);
+                                    &clients, &umodes, creation_time);
 
   if (clients) {
     silc_buffer_push(clients, clients->data - clients->head);
     SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
                            clients->data, clients->len, TRUE);
 
     silc_buffer_free(clients);
   }
+
+  if (umodes) {
+    silc_buffer_push(umodes, umodes->data - umodes->head);
+    SILC_LOG_HEXDUMP(("umodes"), umodes->data, umodes->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           umodes->data, umodes->len, TRUE);
+
+    silc_buffer_free(umodes);
+  }
 }
 
-static SilcBuffer 
-silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
-{
-  va_list ap;
-  SilcBuffer p;
+/* Returns channel's topic for announcing it */
 
-  va_start(ap, argc);
-  p = silc_notify_payload_encode(notify, argc, ap);
-  va_end(ap);
-  return p;
+void silc_server_announce_get_channel_topic(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *topic)
+{
+  SilcBuffer chidp;
+
+  if (channel->topic) {
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+    *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+                                               chidp->data, chidp->len,
+                                               channel->topic,
+                                               strlen(channel->topic));
+    silc_buffer_free(chidp);
+  }
 }
 
 /* Returns assembled packets for channel users of the `channel'. */
@@ -3435,43 +3532,44 @@ void silc_server_announce_get_channel_users(SilcServer server,
     clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
 
     /* JOIN Notify */
-    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2, 
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2,
                                             clidp->data, clidp->len,
                                             chidp->data, chidp->len);
     len = tmp->len;
-    *channel_users = 
-      silc_buffer_realloc(*channel_users, 
-                         (*channel_users ? 
+    *channel_users =
+      silc_buffer_realloc(*channel_users,
+                         (*channel_users ?
                           (*channel_users)->truelen + len : len));
-    silc_buffer_pull_tail(*channel_users, 
-                         ((*channel_users)->end - 
+    silc_buffer_pull_tail(*channel_users,
+                         ((*channel_users)->end -
                           (*channel_users)->data));
-    
+
     silc_buffer_put(*channel_users, tmp->data, tmp->len);
     silc_buffer_pull(*channel_users, len);
     silc_buffer_free(tmp);
 
     /* CUMODE notify for mode change on the channel */
     SILC_PUT32_MSB(chl->mode, mode);
-    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE, 
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
                                             3, clidp->data, clidp->len,
                                             mode, 4,
                                             clidp->data, clidp->len);
     len = tmp->len;
-    *channel_users_modes = 
-      silc_buffer_realloc(*channel_users_modes, 
-                         (*channel_users_modes ? 
+    *channel_users_modes =
+      silc_buffer_realloc(*channel_users_modes,
+                         (*channel_users_modes ?
                           (*channel_users_modes)->truelen + len : len));
-    silc_buffer_pull_tail(*channel_users_modes, 
-                         ((*channel_users_modes)->end - 
+    silc_buffer_pull_tail(*channel_users_modes,
+                         ((*channel_users_modes)->end -
                           (*channel_users_modes)->data));
-    
+
     silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
     silc_buffer_pull(*channel_users_modes, len);
     silc_buffer_free(tmp);
 
     silc_buffer_free(clidp);
   }
+  silc_hash_table_list_reset(&htl);
   silc_buffer_free(chidp);
 }
 
@@ -3484,17 +3582,20 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcBuffer *channels,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
-                                      uint32 *channel_users_modes_c,
-                                      SilcChannelID ***channel_ids)
+                                      SilcUInt32 *channel_users_modes_c,
+                                      SilcBuffer **channel_topics,
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
   SilcChannelEntry channel;
   unsigned char *cid;
-  uint32 id_len;
-  uint16 name_len;
+  SilcUInt32 id_len;
+  SilcUInt16 name_len;
   int len;
   int i = *channel_users_modes_c;
+  bool announce;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -3503,38 +3604,54 @@ void silc_server_announce_get_channels(SilcServer server,
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        channel = (SilcChannelEntry)id_cache->context;
-       
+
+       if (creation_time && channel->created < creation_time)
+         announce = FALSE;
+       else
+         announce = TRUE;
+
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
-       len = 4 + name_len + id_len + 4;
-       *channels = 
-         silc_buffer_realloc(*channels, 
-                             (*channels ? (*channels)->truelen + len : len));
-       silc_buffer_pull_tail(*channels, 
-                             ((*channels)->end - (*channels)->data));
-       silc_buffer_format(*channels,
-                          SILC_STR_UI_SHORT(name_len),
-                          SILC_STR_UI_XNSTRING(channel->channel_name, 
-                                               name_len),
-                          SILC_STR_UI_SHORT(id_len),
-                          SILC_STR_UI_XNSTRING(cid, id_len),
-                          SILC_STR_UI_INT(channel->mode),
-                          SILC_STR_END);
-       silc_buffer_pull(*channels, len);
+       if (announce) {
+         len = 4 + name_len + id_len + 4;
+         *channels =
+           silc_buffer_realloc(*channels,
+                               (*channels ? (*channels)->truelen +
+                                len : len));
+         silc_buffer_pull_tail(*channels,
+                               ((*channels)->end - (*channels)->data));
+         silc_buffer_format(*channels,
+                            SILC_STR_UI_SHORT(name_len),
+                            SILC_STR_UI_XNSTRING(channel->channel_name,
+                                                 name_len),
+                            SILC_STR_UI_SHORT(id_len),
+                            SILC_STR_UI_XNSTRING(cid, id_len),
+                            SILC_STR_UI_INT(channel->mode),
+                            SILC_STR_END);
+         silc_buffer_pull(*channels, len);
+       }
 
+       /* Channel user modes */
        *channel_users_modes = silc_realloc(*channel_users_modes,
-                                           sizeof(**channel_users_modes) * 
+                                           sizeof(**channel_users_modes) *
                                            (i + 1));
        (*channel_users_modes)[i] = NULL;
-       *channel_ids = silc_realloc(*channel_ids, 
+       *channel_ids = silc_realloc(*channel_ids,
                                    sizeof(**channel_ids) * (i + 1));
        (*channel_ids)[i] = NULL;
        silc_server_announce_get_channel_users(server, channel,
                                               channel_users,
-                                              channel_users_modes[i]);
+                                              &(*channel_users_modes)[i]);
        (*channel_ids)[i] = channel->id;
+
+       /* Channel's topic */
+       *channel_topics = silc_realloc(*channel_topics,
+                                      sizeof(**channel_topics) * (i + 1));
+       (*channel_topics)[i] = NULL;
+       silc_server_announce_get_channel_topic(server, channel,
+                                              &(*channel_topics)[i]);
        i++;
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -3550,13 +3667,19 @@ void silc_server_announce_get_channels(SilcServer server,
 
 /* This function is used to announce our existing channels to our router
    when we've connected to it. This also announces the users on the
-   channels to the router. */
-
-void silc_server_announce_channels(SilcServer server)
+   channels to the router. If the `creation_time' is non-zero only the
+   channels that was created after the `creation_time' are announced.
+   Note that the channel users are still announced even if the `creation_time'
+   was provided. */
+
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time,
+                                  SilcSocketConnection remote)
 {
   SilcBuffer channels = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
-  uint32 channel_users_modes_c = 0;
+  SilcBuffer *channel_topics = NULL;
+  SilcUInt32 channel_users_modes_c = 0;
   SilcChannelID **channel_ids = NULL;
 
   SILC_LOG_DEBUG(("Announcing channels and channel users"));
@@ -3566,21 +3689,24 @@ void silc_server_announce_channels(SilcServer server)
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
-                                   &channel_ids);
+                                   &channel_topics,
+                                   &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
-  silc_server_announce_get_channels(server, server->global_list,
-                                   &channels, &channel_users,
-                                   &channel_users_modes,
-                                   &channel_users_modes_c,
-                                   &channel_ids);
+  if (server->server_type != SILC_SERVER)
+    silc_server_announce_get_channels(server, server->global_list,
+                                     &channels, &channel_users,
+                                     &channel_users_modes,
+                                     &channel_users_modes_c,
+                                     &channel_topics,
+                                     &channel_ids, creation_time);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
     SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
                            channels->data, channels->len,
                            FALSE);
@@ -3590,11 +3716,11 @@ void silc_server_announce_channels(SilcServer server)
 
   if (channel_users) {
     silc_buffer_push(channel_users, channel_users->data - channel_users->head);
-    SILC_LOG_HEXDUMP(("channel users"), channel_users->data, 
+    SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
                     channel_users->len);
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
                            channel_users->data, channel_users->len,
                            FALSE);
@@ -3606,27 +3732,53 @@ void silc_server_announce_channels(SilcServer server)
     int i;
 
     for (i = 0; i < channel_users_modes_c; i++) {
-      silc_buffer_push(channel_users_modes[i], 
-                      channel_users_modes[i]->data - 
+      if (!channel_users_modes[i])
+        continue;
+      silc_buffer_push(channel_users_modes[i],
+                      channel_users_modes[i]->data -
                       channel_users_modes[i]->head);
-      SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data, 
+      SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data,
                       channel_users_modes[i]->len);
-      silc_server_packet_send_dest(server, server->router->connection,
+      silc_server_packet_send_dest(server, remote,
                                   SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
                                   channel_ids[i], SILC_ID_CHANNEL,
-                                  channel_users_modes[i]->data, 
+                                  channel_users_modes[i]->data,
                                   channel_users_modes[i]->len,
                                   FALSE);
       silc_buffer_free(channel_users_modes[i]);
     }
     silc_free(channel_users_modes);
-    silc_free(channel_ids);
   }
+
+  if (channel_topics) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_topics[i])
+       continue;
+
+      silc_buffer_push(channel_topics[i],
+                      channel_topics[i]->data -
+                      channel_topics[i]->head);
+      SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data,
+                      channel_topics[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_topics[i]->data,
+                                  channel_topics[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_topics[i]);
+    }
+    silc_free(channel_topics);
+  }
+
+  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. 
+   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). */
@@ -3649,21 +3801,22 @@ void silc_server_get_users_on_channel(SilcServer server,
                                      SilcChannelEntry channel,
                                      SilcBuffer *user_list,
                                      SilcBuffer *mode_list,
-                                     uint32 *user_count)
+                                     SilcUInt32 *user_count)
 {
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   SilcBuffer idp;
-  uint32 list_count = 0, len = 0;
+  SilcUInt32 list_count = 0, len = 0;
 
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl))
     len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
+  silc_hash_table_list_reset(&htl);
 
   client_id_list = silc_buffer_alloc(len);
-  client_mode_list = 
+  client_mode_list =
     silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
   silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
   silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
@@ -3682,9 +3835,10 @@ void silc_server_get_users_on_channel(SilcServer server,
 
     list_count++;
   }
-  silc_buffer_push(client_id_list, 
+  silc_hash_table_list_reset(&htl);
+  silc_buffer_push(client_id_list,
                   client_id_list->data - client_id_list->head);
-  silc_buffer_push(client_mode_list, 
+  silc_buffer_push(client_mode_list,
                   client_mode_list->data - client_mode_list->head);
 
   *user_list = client_id_list;
@@ -3700,23 +3854,23 @@ void silc_server_save_users_on_channel(SilcServer server,
                                       SilcClientID *noadd,
                                       SilcBuffer user_list,
                                       SilcBuffer mode_list,
-                                      uint32 user_count)
+                                      SilcUInt32 user_count)
 {
   int i;
+  SilcUInt16 idp_len;
+  SilcUInt32 mode;
+  SilcClientID *client_id;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache;
+  bool global;
 
-  /* Cache the received Client ID's and modes. This cache expires
-     whenever server sends notify message to channel. It means two things;
-     some user has joined or leaved the channel. XXX TODO! */
-  for (i = 0; i < user_count; i++) {
-    uint16 idp_len;
-    uint32 mode;
-    SilcClientID *client_id;
-    SilcClientEntry client;
+  SILC_LOG_DEBUG(("Start"));
 
+  for (i = 0; i < user_count; i++) {
     /* Client ID */
     SILC_GET16_MSB(idp_len, user_list->data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(user_list->data, idp_len);
+    client_id = silc_id_payload_parse_id(user_list->data, idp_len, NULL);
     silc_buffer_pull(user_list, idp_len);
     if (!client_id)
       continue;
@@ -3729,13 +3883,18 @@ void silc_server_save_users_on_channel(SilcServer server,
       silc_free(client_id);
       continue;
     }
-    
+
+    global = FALSE;
+
     /* Check if we have this client cached already. */
     client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                          NULL);
-    if (!client)
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, NULL);
+                                          server->server_type, &cache);
+    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. */
@@ -3745,22 +3904,28 @@ void silc_server_save_users_on_channel(SilcServer server,
       }
 
       /* We don't have that client anywhere, add it. The client is added
-        to global list since server didn't have it in the lists so it must be 
+        to global list since server didn't have it in the lists so it must be
         global. */
       client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
-                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
-                                     sock->user_data, NULL);
+                                     silc_id_dup(client_id, SILC_ID_CLIENT),
+                                     sock->user_data, NULL, 0);
       if (!client) {
+       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
        silc_free(client_id);
        continue;
       }
 
-      client->data.registered = TRUE;
+      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;
     }
 
     silc_free(client_id);
 
-    if (!silc_server_client_on_channel(client, channel)) {
+    if (!silc_server_client_on_channel(client, channel, NULL)) {
       /* Client was not on the channel, add it. */
       SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
       chl->client = client;
@@ -3768,6 +3933,7 @@ void silc_server_save_users_on_channel(SilcServer server,
       chl->channel = channel;
       silc_hash_table_add(channel->user_list, chl->client, chl);
       silc_hash_table_add(client->channels, chl->channel, chl);
+      channel->user_count++;
     }
   }
 }
@@ -3779,7 +3945,7 @@ void silc_server_save_users_on_channel(SilcServer server,
 
 SilcSocketConnection silc_server_get_client_route(SilcServer server,
                                                  unsigned char *id_data,
-                                                 uint32 id_len,
+                                                 SilcUInt32 id_len,
                                                  SilcClientID *client_id,
                                                  SilcIDListData *idata)
 {
@@ -3801,13 +3967,10 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
 
   /* If the destination belongs to our server we don't have to route
      the packet anywhere but to send it to the local destination. */
-  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+  client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
   if (client) {
     silc_free(id);
 
-    if (client->data.registered == FALSE)
-      return NULL;
-
     /* If we are router and the client has router then the client is in
        our cell but not directly connected to us. */
     if (server->server_type == SILC_ROUTER && client->router) {
@@ -3827,18 +3990,19 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
 
   /* Destination belongs to someone not in this server. If we are normal
      server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
+  if (server->server_type != SILC_ROUTER && !server->standalone) {
     silc_free(id);
     if (idata)
       *idata = (SilcIDListData)server->router;
     return server->router->connection;
   }
 
-  /* We are router and we will perform route lookup for the destination 
+  /* We are router and we will perform route lookup for the destination
      and send the packet to fastest route. */
   if (server->server_type == SILC_ROUTER && !server->standalone) {
     /* Check first that the ID is valid */
-    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+    client = silc_idlist_find_client_by_id(server->global_list, id,
+                                          TRUE, NULL);
     if (client) {
       SilcSocketConnection dst_sock;
 
@@ -3866,28 +4030,29 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
   unsigned char *cid;
-  uint32 id_len;
-  uint16 name_len;
+  SilcUInt32 id_len;
+  SilcUInt16 name_len;
   int len;
 
   silc_hash_table_list(client->channels, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     channel = chl->channel;
 
-    if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+    if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
+       channel->mode & SILC_CHANNEL_MODE_PRIVATE)
       continue;
 
     cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
     id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
     name_len = strlen(channel->channel_name);
-    
+
     len = 4 + name_len + id_len + 4;
-    buffer = silc_buffer_realloc(buffer, 
+    buffer = silc_buffer_realloc(buffer,
                                 (buffer ? (buffer)->truelen + len : len));
     silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
     silc_buffer_format(buffer,
                       SILC_STR_UI_SHORT(name_len),
-                      SILC_STR_UI_XNSTRING(channel->channel_name, 
+                      SILC_STR_UI_XNSTRING(channel->channel_name,
                                            name_len),
                       SILC_STR_UI_SHORT(id_len),
                       SILC_STR_UI_XNSTRING(cid, id_len),
@@ -3896,6 +4061,7 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
     silc_buffer_pull(buffer, len);
     silc_free(cid);
   }
+  silc_hash_table_list_reset(&htl);
 
   if (buffer)
     silc_buffer_push(buffer, buffer->data - buffer->head);
@@ -3907,14 +4073,19 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
    it using WHOIS command. */
 
 SilcClientEntry silc_server_get_client_resolve(SilcServer server,
-                                              SilcClientID *client_id)
+                                              SilcClientID *client_id,
+                                              bool *resolved)
 {
   SilcClientEntry client;
 
-  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+  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, NULL);
+    client = silc_idlist_find_client_by_id(server->global_list,
+                                          client_id, TRUE, NULL);
     if (!client && server->server_type == SILC_ROUTER)
       return NULL;
   }
@@ -3925,9 +4096,13 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
   if (!client || !client->nickname || !client->username) {
     SilcBuffer buffer, idp;
 
+    client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+    client->resolve_cmd_ident = ++server->cmd_ident;
+
     idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
-                                           ++server->cmd_ident, 1,
+                                           server->cmd_ident, 1,
                                            3, idp->data, idp->len);
     silc_server_packet_send(server, client ? client->router->connection :
                            server->router->connection,
@@ -3935,6 +4110,10 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
                            buffer->data, buffer->len, FALSE);
     silc_buffer_free(idp);
     silc_buffer_free(buffer);
+
+    if (resolved)
+      *resolved = TRUE;
+
     return NULL;
   }
 
@@ -3961,21 +4140,21 @@ SILC_TASK_CALLBACK(silc_server_rekey_callback)
   proto_ctx->sock = sock;
   proto_ctx->responder = FALSE;
   proto_ctx->pfs = idata->rekey->pfs;
-      
+
   /* Perform rekey protocol. Will call the final callback after the
      protocol is over. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY,
                      &protocol, proto_ctx, silc_server_rekey_final);
   sock->protocol = protocol;
-      
+
   /* Run the protocol */
   silc_protocol_execute(protocol, server->schedule, 0, 0);
 
   /* 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);
+  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
@@ -3994,6 +4173,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
   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_protocol_cancel(protocol, server->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
@@ -4005,6 +4185,10 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     return;
   }
 
+  /* 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);
+
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;