updates.
[silc.git] / apps / silcd / server.c
index a6ae79ff22a1dc677976eb4d994219a62ab66f1d..2e85203df1d7335de7d244086c677cfb91a385b4 100644 (file)
@@ -2,7 +2,7 @@
 
   server.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
@@ -39,6 +39,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process);
 SILC_TASK_CALLBACK(silc_server_packet_parse_real);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
 SILC_TASK_CALLBACK(silc_server_failure_callback);
+SILC_TASK_CALLBACK(silc_server_rekey_callback);
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -76,13 +77,14 @@ void silc_server_free(SilcServer server)
     SilcSimContext *sim;
 #endif
 
-    if (server->local_list)
-      silc_free(server->local_list);
-    if (server->global_list)
-      silc_free(server->global_list);
+    silc_free(server->local_list);
+    silc_free(server->global_list);
     if (server->rng)
       silc_rng_free(server->rng);
 
+    if (server->pkcs)
+      silc_pkcs_free(server->pkcs);
+
 #ifdef SILC_SIM
     while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
       silc_dlist_del(server->sim, sim);
@@ -91,8 +93,7 @@ void silc_server_free(SilcServer server)
     silc_dlist_uninit(server->sim);
 #endif
 
-    if (server->params)
-      silc_free(server->params);
+    silc_free(server->params);
 
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
@@ -114,11 +115,22 @@ int silc_server_init(SilcServer server)
   SilcServerID *id;
   SilcServerEntry id_entry;
   SilcIDListPurge purge;
+  SilcServerConfigSectionListenPort *listen;
 
   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));
@@ -134,10 +146,14 @@ int silc_server_init(SilcServer server)
   silc_server_config_setlogfiles(server->config);
  
   /* Register all configured ciphers, PKCS and hash functions. */
-  silc_server_config_register_ciphers(server->config);
-  silc_server_config_register_pkcs(server->config);
-  silc_server_config_register_hashfuncs(server->config);
-  silc_server_config_register_hmacs(server->config);
+  if (!silc_server_config_register_ciphers(server->config))
+    silc_cipher_register_default();
+  if (!silc_server_config_register_pkcs(server->config))
+    silc_pkcs_register_default();
+  if (!silc_server_config_register_hashfuncs(server->config))
+    silc_hash_register_default();
+  if (!silc_server_config_register_hmacs(server->config))
+    silc_hmac_register_default();
 
   /* Initialize random number generator for the server. */
   server->rng = silc_rng_alloc();
@@ -151,89 +167,42 @@ int silc_server_init(SilcServer server)
   /* Initialize none cipher */
   silc_cipher_alloc("none", &server->none_cipher);
 
-  /* XXXXX Generate RSA key pair */
-  {
-    unsigned char *public_key;
-    unsigned char *private_key;
-    unsigned int pk_len, prv_len;
-    struct stat st;
-
-    if (stat("pubkey.pub", &st) < 0 && stat("privkey.prv", &st) < 0) {
-
-      if (silc_pkcs_alloc("rsa", &server->pkcs) == FALSE) {
-       SILC_LOG_ERROR(("Could not create RSA key pair"));
-       goto err0;
-      }
-      
-      if (server->pkcs->pkcs->init(server->pkcs->context, 
-                                  1024, server->rng) == FALSE) {
-       SILC_LOG_ERROR(("Could not generate RSA key pair"));
-       goto err0;
-      }
-      
-      public_key = server->pkcs->pkcs->get_public_key(server->pkcs->context,
-                                                     &pk_len);
-      private_key = server->pkcs->pkcs->get_private_key(server->pkcs->context,
-                                                       &prv_len);
-      
-      SILC_LOG_HEXDUMP(("public key"), public_key, pk_len);
-      SILC_LOG_HEXDUMP(("private key"), private_key, prv_len);
-      
-      server->public_key = 
-       silc_pkcs_public_key_alloc("rsa", "UN=root, HN=dummy",
-                                  public_key, pk_len);
-      server->private_key = 
-       silc_pkcs_private_key_alloc("rsa", private_key, prv_len);
-      
-      /* XXX Save keys */
-      silc_pkcs_save_public_key("pubkey.pub", server->public_key,
-                               SILC_PKCS_FILE_PEM);
-      silc_pkcs_save_private_key("privkey.prv", server->private_key, NULL,
-                                SILC_PKCS_FILE_BIN);
-
-      memset(public_key, 0, pk_len);
-      memset(private_key, 0, prv_len);
-      silc_free(public_key);
-      silc_free(private_key);
-    } else {
-      silc_pkcs_load_public_key("pubkey.pub", &server->public_key,
-                               SILC_PKCS_FILE_PEM);
-      silc_pkcs_load_private_key("privkey.prv", &server->private_key,
-                                SILC_PKCS_FILE_BIN);
-    }
-  }
+  /* Allocate PKCS context for local public and private keys */
+  silc_pkcs_alloc(server->public_key->name, &server->pkcs);
+  silc_pkcs_public_key_set(server->pkcs, server->public_key);
+  silc_pkcs_private_key_set(server->pkcs, server->private_key);
 
-  /* 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. */
+  /* Create a listening server. Note that our server can listen on multiple
+     ports. All listeners are created here and now. */
   sock_count = 0;
-  while(server->config->listen_port) {
+  listen = server->config->listen_port;
+  while(listen) {
     int tmp;
 
     tmp = silc_net_create_server(server->config->listen_port->port,
-                                server->config->listen_port->host);
+                                server->config->listen_port->listener_ip);
     if (tmp < 0)
       goto err0;
 
     sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
     sock[sock_count] = tmp;
-    server->config->listen_port = server->config->listen_port->next;
     sock_count++;
+    listen = listen->next;
   }
 
   /* Initialize ID caches */
   server->local_list->clients = 
-    silc_idcache_alloc(0, silc_idlist_client_destructor);
-  server->local_list->servers = silc_idcache_alloc(0, NULL);
-  server->local_list->channels = silc_idcache_alloc(0, NULL);
+    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 
      router these are used as they are supposed to be used on router. */
   server->global_list->clients = 
-    silc_idcache_alloc(0, silc_idlist_client_destructor);
-  server->global_list->servers = silc_idcache_alloc(0, NULL);
-  server->global_list->channels = silc_idcache_alloc(0, NULL);
+    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 
@@ -257,7 +226,7 @@ int silc_server_init(SilcServer server)
     
     server->id = id;
     server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
-    server->id_string_len = silc_id_get_len(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;
 
@@ -273,6 +242,7 @@ int silc_server_init(SilcServer server)
       SILC_LOG_ERROR(("Could not add ourselves to cache"));
       goto err0;
     }
+    id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
     
     /* Add ourselves also to the socket table. The entry allocated above
        is sent as argument for fast referencing in the future. */
@@ -280,6 +250,23 @@ int silc_server_init(SilcServer server)
                      &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. */
@@ -287,54 +274,49 @@ int silc_server_init(SilcServer server)
     server->id_entry = id_entry;
   }
 
-  /* Register the task queues. In SILC we have by default three task queues. 
-     One task queue for non-timeout tasks which perform different kind of 
-     I/O on file descriptors, timeout task queue for timeout tasks, and,
-     generic non-timeout task queue whose tasks apply to all connections. */
-  silc_task_queue_alloc(&server->io_queue, TRUE);
-  if (!server->io_queue) {
-    goto err0;
-  }
-  silc_task_queue_alloc(&server->timeout_queue, TRUE);
-  if (!server->timeout_queue) {
-    goto err1;
-  }
-  silc_task_queue_alloc(&server->generic_queue, TRUE);
-  if (!server->generic_queue) {
-    goto err1;
-  }
-
   /* Register protocols */
   silc_server_protocols_register();
 
-  /* Initialize the scheduler */
-  silc_schedule_init(&server->io_queue, &server->timeout_queue, 
-                    &server->generic_queue, 
-                    SILC_SERVER_MAX_CONNECTIONS);
+  /* Initialize the scheduler. */
+  server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
+  if (!server->schedule)
+    goto err0;
   
-  /* Add the first task to the queue. This is task that is executed by
+  /* 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_task_register(server->timeout_queue, sock[0], 
-                    silc_server_connect_to_router,
-                    (void *)server, 0, 1,
-                    SILC_TASK_TIMEOUT,
-                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(server->schedule, sock[0], 
+                        silc_server_connect_to_router,
+                        (void *)server, 0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 
-  /* Add listener task to the queue. This task receives new connections to the 
-     server. This task remains on the queue until the end of the program. */
-  silc_task_register(server->io_queue, sock[0],
-                    silc_server_accept_new_connection,
-                    (void *)server, 0, 0, 
-                    SILC_TASK_FD,
-                    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 
+     program. */
+  silc_schedule_task_add(server->schedule, sock[0],
+                        silc_server_accept_new_connection,
+                        (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) {
+    SilcServerConfigSectionServerConnection *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;
+       break;
+      }
+      ptr = ptr->next;
+    }
+  }
 
   /* Register the ID Cache purge task. This periodically purges the ID cache
      and removes the expired cache entries. */
@@ -342,29 +324,26 @@ int silc_server_init(SilcServer server)
   /* Clients local list */
   purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->local_list->clients;
-  purge->timeout_queue = server->timeout_queue;
-  silc_task_register(purge->timeout_queue, 0, 
-                    silc_idlist_purge,
-                    (void *)purge, 600, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  purge->schedule = server->schedule;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, 600, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   /* Clients global list */
   purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->global_list->clients;
-  purge->timeout_queue = server->timeout_queue;
-  silc_task_register(purge->timeout_queue, 0, 
-                    silc_idlist_purge,
-                    (void *)purge, 300, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  purge->schedule = server->schedule;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, 300, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
   return TRUE;
 
-  silc_task_queue_free(server->timeout_queue);
- err1:
-  silc_task_queue_free(server->io_queue);
  err0:
   for (i = 0; i < sock_count; i++)
     silc_net_close_server(sock[i]);
@@ -476,25 +455,6 @@ void silc_server_daemonise(SilcServer server)
   }
 }
 
-/* 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. */
-
-void silc_server_stop(SilcServer server)
-{
-  SILC_LOG_DEBUG(("Stopping server"));
-
-  /* Stop the scheduler, although it might be already stopped. This
-     doesn't hurt anyone. This removes all the tasks and task queues,
-     as well. */
-  silc_schedule_stop();
-  silc_schedule_uninit();
-
-  silc_server_protocols_unregister();
-
-  SILC_LOG_DEBUG(("Server stopped"));
-}
-
 /* 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. */
@@ -503,71 +463,41 @@ 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
      the program will be terminated. */
-  silc_schedule();
+  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;
-  }
-  sconn->retry_count++;
-  sconn->retry_timeout = sconn->retry_timeout +
-    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
+  silc_schedule_stop(server->schedule);
+  silc_schedule_uninit(server->schedule);
 
-  /* 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_task_register(server->timeout_queue, 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;
-
-  /* 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_task_register(server->timeout_queue, fd, 
-                      silc_server_connect_to_router_retry,
-                      context, 0, 1, SILC_TASK_TIMEOUT, 
-                      SILC_TASK_PRI_NORMAL);
-    return;
-  }
+  void *context;
 
   /* Set socket options */
   silc_net_set_socket_nonblock(sock);
@@ -603,7 +533,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   /* Register a timeout task that will be executed if the protocol
      is not executed within set limit. */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock, 
+    silc_schedule_task_add(server->schedule, sock, 
                       silc_server_timeout_remote,
                       server, server->params->protocol_timeout,
                       server->params->protocol_timeout_usec,
@@ -620,7 +550,76 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   SILC_REGISTER_CONNECTION_FOR_IO(sock);
   
   /* Run the protocol */
-  protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+  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;
+
+  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 > SILC_SERVER_RETRY_INTERVAL_MAX)
+      sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
+  } else {
+    sconn->retry_timeout = server->params->retry_interval_min;
+  }
+  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;
+  }
+
+  /* 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);
+}
+
+/* Generic routine to use connect to a router. */
+
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{    
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+  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(0);
+
+  /* Connect to remote host */
+  sock = silc_net_create_connection(server->config->listen_port->local_ip,
+                                   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;
+  }
+
+  /* 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
@@ -632,64 +631,45 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
+  SilcServerConfigSectionServerConnection *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) {
+  /* Create the connections to all our routes */
+  ptr = server->config->routers;
+  while (ptr) {
+    
+    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) {
       /* 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_task_register(server->timeout_queue, fd, 
-                        silc_server_connect_router,
-                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                        SILC_TASK_PRI_NORMAL);
-      return;
+      sconn->remote_host = strdup(ptr->host);
+      sconn->remote_port = ptr->port;
+      sconn->backup = ptr->backup_router;
+      
+      silc_schedule_task_add(server->schedule, fd, 
+                            silc_server_connect_router,
+                            (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                            SILC_TASK_PRI_NORMAL);
     }
-  }
-
-  /* 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_task_register(server->timeout_queue, fd, 
-                          silc_server_connect_router,
-                          (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                          SILC_TASK_PRI_NORMAL);
-      }
-
-      if (!ptr->next)
-       return;
-
-      ptr = ptr->next;
-    }
+    if (!ptr->next)
+      return;
+    
+    ptr = ptr->next;
   }
 
   SILC_LOG_DEBUG(("No router(s), server will be standalone"));
@@ -709,24 +689,26 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
-  SilcSocketConnection sock = NULL;
+  SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
+  SilcServerConfigSectionServerConnection *conn = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     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_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -740,18 +722,19 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
                                        ctx->ske->prop->hmac,
+                                       ctx->ske->prop->group,
                                        ctx->responder)) {
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     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_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -763,33 +746,39 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
   proto_ctx->context = (void *)sconn;
-  proto_ctx->sock = sock = server->sockets[fd];
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;      /* Save SKE object from previous protocol */
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
 
-  /* Resolve the authentication method used in this connection */
-  proto_ctx->auth_meth = SILC_AUTH_PASSWORD;
-  if (server->config->routers) {
-    SilcServerConfigSectionServerConnection *conn = NULL;
-
-    /* Check if we find a match from user configured connections */
-    conn = silc_server_config_find_router_conn(server->config,
-                                              sock->hostname,
-                                              sock->port);
-    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);
-      }
-    } else {
-      /* No match found. */
-      /* XXX */
+  /* 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 (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);
     }
   } else {
-    /* XXX */
+    SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
+                   sock->hostname, sock->ip, sock->port));
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_free(ctx->dest_id);
+    silc_free(ctx);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Key exchange failed");
+    return;
   }
 
   /* Free old protocol as it is finished now */
@@ -811,15 +800,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
      this timelimit the connection will be terminated. Currently
      this is 15 seconds and is hard coded limit (XXX). */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock->sock, 
+    silc_schedule_task_add(server->schedule, sock->sock, 
                       silc_server_timeout_remote,
                       (void *)server, 15, 0,
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_LOW);
 
   /* Run the protocol */
-  sock->protocol->execute(server->timeout_queue, 0, 
-                         sock->protocol, sock->sock, 0, 0);
+  silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 }
 
 /* Finalizes the connection to router. Registers a server task to the
@@ -837,13 +825,15 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcBuffer packet;
   SilcServerHBContext hb_context;
   unsigned char *id_string;
+  uint32 id_len;
+  SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  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;
@@ -851,24 +841,24 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* 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_task_register(server->io_queue, server->sock, 
-                      silc_server_accept_new_connection,
-                      (void *)server, 0, 0, 
-                      SILC_TASK_FD,
-                      SILC_TASK_PRI_NORMAL);
+  if (!server->listenning && !sconn->backup) {
+    silc_schedule_task_add(server->schedule, server->sock, 
+                          silc_server_accept_new_connection,
+                          (void *)server, 0, 0, 
+                          SILC_TASK_FD,
+                          SILC_TASK_PRI_NORMAL);
     server->listenning = TRUE;
   }
 
   /* Send NEW_SERVER packet to the router. We will become registered
      to the SILC network after sending this packet. */
   id_string = silc_id_id2str(server->id, SILC_ID_SERVER);
-  packet = silc_buffer_alloc(2 + 2 + SILC_ID_SERVER_LEN + 
-                            strlen(server->server_name));
+  id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
+  packet = silc_buffer_alloc(2 + 2 + id_len + strlen(server->server_name));
   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
   silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(SILC_ID_SERVER_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_SERVER_LEN),
+                    SILC_STR_UI_SHORT(id_len),
+                    SILC_STR_UI_XNSTRING(id_string, id_len),
                     SILC_STR_UI_SHORT(strlen(server->server_name)),
                     SILC_STR_UI_XNSTRING(server->server_name,
                                          strlen(server->server_name)),
@@ -880,15 +870,13 @@ 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));
 
   /* Add the connected router to local server list */
-  server->standalone = FALSE;
-  id_entry = silc_idlist_add_server(server->local_list, sock->hostname,
+  id_entry = silc_idlist_add_server(server->local_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_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
@@ -898,9 +886,8 @@ 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;
-  server->router->data.registered = TRUE;
+  idata = (SilcIDListData)sock->user_data;
+  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 
   /* Perform keepalive. The `hb_context' will be freed automatically
      when finally calling the silc_socket_free function. XXX hardcoded 
@@ -909,15 +896,40 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   hb_context->server = server;
   silc_socket_set_heartbeat(sock, 600, hb_context,
                            silc_server_perform_heartbeat,
-                           server->timeout_queue);
+                           server->schedule);
+
+  /* Register re-key timeout */
+  idata->rekey->timeout = 3600; /* XXX hardcoded */
+  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);
 
-  /* 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);
+
+    /* Announce our clients and channels to the router */
+    silc_server_announce_clients(server, 0);
+    silc_server_announce_channels(server, 0);
+  } else {
+    /* Add this server to be our backup router */
+    silc_server_backup_add(server, id_entry, FALSE);
+  }
 
-  /* 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 */
@@ -936,6 +948,126 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   sock->protocol = NULL;
 }
 
+/* Host lookup callbcak 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 
+silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
+                                        void *context)
+{
+  SilcServer server = (SilcServer)context;
+  SilcServerKEInternalContext *proto_ctx;
+  void *cconfig, *sconfig, *rconfig;
+  SilcServerConfigSectionDenyConnection *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)) {
+    SILC_LOG_ERROR(("IP/DNS lookup failed %s",
+                   sock->hostname ? sock->hostname :
+                   sock->ip ? sock->ip : ""));
+    server->stat.conn_failures++;
+    silc_server_disconnect_remote(server, sock,
+                                 "Server closed connection: Unknown host");
+    return;
+  }
+
+  /* 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 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,
+                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);
+  if (!deny)
+    deny = silc_server_config_denied_conn(server->config, sock->hostname,
+                                         port);
+  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 :
+                                 "Server closed connection: "
+                                 "Connection refused");
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* Check whether we have configred 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 && !sconfig && !rconfig) {
+    silc_server_disconnect_remote(server, sock, 
+                                 "Server closed connection: "
+                                 "Connection refused");
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* The connection is allowed */
+
+  /* Allocate internal context for key exchange protocol. This is
+     sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = context;
+  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;
+
+  /* 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 
+     there before we start the protocol. */
+  server->stat.auth_attempts++;
+  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, 
+                          silc_server_timeout_remote,
+                          context, 60, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
+}
+
 /* Accepts new connections to the server. Accepting new connections are
    done in three parts to make it async. */
 
@@ -943,7 +1075,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 {
   SilcServer server = (SilcServer)context;
   SilcSocketConnection newsocket;
-  SilcServerKEInternalContext *proto_ctx;
   int sock;
 
   SILC_LOG_DEBUG(("Accepting new connection"));
@@ -959,13 +1090,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   /* Check max connections */
   if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    if (server->config->redirect) {
-      /* XXX Redirecting connection to somewhere else now?? */
-      /*silc_server_send_notify("Server is full, trying to redirect..."); */
-    } else {
-      SILC_LOG_ERROR(("Refusing connection, server is full"));
-      server->stat.conn_failures++;
-    }
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
     return;
   }
 
@@ -978,59 +1104,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
   server->sockets[sock] = newsocket;
 
-  /* XXX This MUST be done async as this will block the entire server
-     process. Either we have to do our own resolver stuff or in the future
-     we can use threads. */
-  /* Perform name and address lookups for the remote host. */
-  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-      !newsocket->ip) {
-    SILC_LOG_ERROR(("IP/DNS lookup failed"));
-    server->stat.conn_failures++;
-    return;
-  }
-  if (!newsocket->hostname)
-    newsocket->hostname = strdup(newsocket->ip);
-  newsocket->port = silc_net_get_remote_port(sock);
-
-  SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
-                newsocket->ip));
-
-  /* Allocate internal context for key exchange protocol. This is
-     sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->server = context;
-  proto_ctx->sock = newsocket;
-  proto_ctx->rng = server->rng;
-  proto_ctx->responder = TRUE;
-
-  /* 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 
-     there before we start the protocol. */
-  server->stat.auth_attempts++;
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &newsocket->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_task_register(server->timeout_queue, newsocket->sock, 
-                      silc_server_timeout_remote,
-                      context, 60, 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 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);
+  /* 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, 
+                         server->schedule);
 }
 
 /* Second part of accepting new connection. Key exchange protocol has been
@@ -1045,7 +1124,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = NULL;
+  SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1054,16 +1133,16 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     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_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
@@ -1078,18 +1157,19 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
                                        ctx->ske->prop->hmac,
+                                       ctx->ske->prop->group,
                                        ctx->responder)) {
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     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_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
@@ -1101,11 +1181,14 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
      is sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
-  proto_ctx->sock = sock = server->sockets[fd];
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;   /* Save SKE object from previous protocol */
   proto_ctx->responder = TRUE;
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
+  proto_ctx->cconfig = ctx->cconfig;
+  proto_ctx->sconfig = ctx->sconfig;
+  proto_ctx->rconfig = ctx->rconfig;
 
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
@@ -1126,11 +1209,11 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
      this timelimit the connection will be terminated. Currently
      this is 60 seconds and is hard coded limit (XXX). */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock->sock, 
-                      silc_server_timeout_remote,
-                      (void *)server, 60, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+    silc_schedule_task_add(server->schedule, sock->sock, 
+                          silc_server_timeout_remote,
+                          (void *)server, 60, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
 }
 
 /* Final part of accepting new connection. The connection has now
@@ -1145,6 +1228,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
   SilcServerHBContext hb_context;
+  SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
   void *id_entry = NULL;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1153,25 +1237,26 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     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_free(ctx);
     if (sock)
       sock->protocol = NULL;
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     server->stat.auth_failures++;
     return;
   }
 
-  sock->type = ctx->conn_type;
-  switch(sock->type) {
+  entry->data.last_receive = time(NULL);
+
+  switch (ctx->conn_type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry client;
@@ -1184,11 +1269,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         and other information is created after we have received NEW_CLIENT
         packet from client. */
       client = silc_idlist_add_client(server->local_list, 
-                                     NULL, 0, NULL, NULL, NULL, NULL, sock);
+                                     NULL, NULL, NULL, NULL, NULL, sock);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
-       break;
+       silc_server_disconnect_remote(server, sock, 
+                                     "Server closed connection: "
+                                     "Authentication failed");
+       server->stat.auth_failures++;
+       goto out;
       }
 
       /* Statistics */
@@ -1204,13 +1293,18 @@ 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;
 
       SILC_LOG_DEBUG(("Remote host is %s", 
-                     sock->type == SILC_SOCKET_TYPE_SERVER ? 
-                     "server" : "router"));
+                     ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
+                     "server" : (conn->backup_router ? 
+                                 "backup router" : "router")));
       SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
-                    sock->ip, sock->type == SILC_SOCKET_TYPE_SERVER ? 
-                    "server" : "router"));
+                    sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
+                    "server" : (conn->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
@@ -1218,30 +1312,56 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         are router. */
       new_server = 
        silc_idlist_add_server(server->local_list, NULL,
-                              sock->type == SILC_SOCKET_TYPE_SERVER ?
+                              ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
                               SILC_SERVER : SILC_ROUTER, NULL, 
-                              sock->type == SILC_SOCKET_TYPE_SERVER ?
+                              ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
                               server->id_entry : NULL, sock);
       if (!new_server) {
        SILC_LOG_ERROR(("Could not add new server to cache"));
        silc_free(sock->user_data);
-       break;
+       silc_server_disconnect_remote(server, sock, 
+                                     "Server closed connection: "
+                                     "Authentication failed");
+       server->stat.auth_failures++;
+       goto out;
       }
 
       /* Statistics */
-      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER)
        server->stat.my_servers++;
       else
        server->stat.my_routers++;
       server->stat.servers++;
 
       id_entry = (void *)new_server;
-      
-      /* There is connection to other server now, if it is router then
-        we will have connection to outside world.  If we are router but
-        normal server connected to us then we will remain standalone,
-        if we are standlone. */
-      if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
+
+      /* 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 && conn->backup_router) {
+       silc_server_backup_add(server, new_server, conn->backup_local);
+
+       /* Change it back to SERVER type since that's what it really is. */
+       if (conn->backup_local) {
+         ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
+         new_server->server_type = SILC_SOCKET_TYPE_SERVER;
+       }
+      }
+
+#if 0
+      /* If the incoming connection is normal server and marked as backup
+        server then it will use us as backup router. We'll disable the
+        connection until it is allowed to be used. */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER && conn->backup_router)
+       SILC_SET_DISABLED(sock);
+#endif
+
+      /* Check whether this connection is to be our primary router connection
+        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)
+         break;
+
        SILC_LOG_DEBUG(("We are not standalone server anymore"));
        server->standalone = FALSE;
        if (!server->id_entry->router) {
@@ -1249,12 +1369,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          server->router = id_entry;
        }
       }
+
       break;
     }
   default:
     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);
@@ -1273,17 +1396,17 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   hb_context->server = server;
   silc_socket_set_heartbeat(sock, 600, hb_context,
                            silc_server_perform_heartbeat,
-                           server->timeout_queue);
+                           server->schedule);
 
-  silc_task_unregister_by_callback(server->timeout_queue,
-                                  silc_server_failure_callback);
+ out:
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    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_free(ctx);
   sock->protocol = NULL;
 }
@@ -1317,21 +1440,29 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (sock->outbuf->data - sock->outbuf->head)
      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    ret = silc_server_packet_send_real(server, sock, TRUE);
+    /* Send the packet */
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
 
-    if (ret == -1)
+    if (ret == -1) {
+      SILC_LOG_ERROR(("Error sending packet to 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")));
       return;
+    }
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
        available for this connection it will be set for output as well. 
        This call clears the output setting and sets it only for input. */
-    SILC_SET_CONNECTION_FOR_INPUT(fd);
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
     SILC_UNSET_OUTBUF_PENDING(sock);
 
     silc_buffer_clear(sock->outbuf);
@@ -1342,9 +1473,18 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
   /* Read some data from connection */
   ret = silc_packet_receive(sock);
-  if (ret < 0)
+  if (ret < 0) {
+
+    if (ret == -1)
+      SILC_LOG_ERROR(("Error receiving packet from 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")));
     return;
-    
+  }    
+
   /* EOF */
   if (ret == 0) {
     SILC_LOG_DEBUG(("Read EOF"));
@@ -1361,16 +1501,6 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     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_task_register(server->timeout_queue, 0, 
-                        silc_server_connect_to_router,
-                        context, 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);
@@ -1389,9 +1519,8 @@ 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;
+    hmac = idata->hmac_receive;
   }
  
   /* Process the packet. This will call the parser that will then
@@ -1420,9 +1549,12 @@ static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
 
      all other packets are special packets 
   */
-  if ((packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
-       !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) ||
-      packet_type != SILC_PACKET_CHANNEL_MESSAGE || 
+
+  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))
@@ -1430,7 +1562,7 @@ static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
 
   return FALSE;
 }
-
+  
 /* Parses whole packet, received earlier. */
 
 SILC_TASK_CALLBACK(silc_server_packet_parse_real)
@@ -1439,15 +1571,28 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   SilcServer server = (SilcServer)parse_ctx->context;
   SilcSocketConnection sock = parse_ctx->sock;
   SilcPacketContext *packet = parse_ctx->packet;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
   int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Decrypt the received packet */
-  ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac, 
+  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)
+  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;
+  }
+
+  /* If entry is disabled ignore what we got. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
     goto out;
 
   if (ret == 0) {
@@ -1468,7 +1613,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     if (client && client->id) {
       void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
                                packet->src_id_type);
-      if (SILC_ID_CLIENT_COMPARE(client->id, id)) {
+      if (!id || !SILC_ID_CLIENT_COMPARE(client->id, id)) {
        silc_free(id);
        goto out;
       }
@@ -1479,9 +1624,10 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   if (server->server_type == SILC_ROUTER) {
     /* Route the packet if it is not destined to us. Other ID types but
        server are handled separately after processing them. */
-    if (packet->dst_id_type == SILC_ID_SERVER && 
+    if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
+       packet->dst_id_type == SILC_ID_SERVER && 
        sock->type != SILC_SOCKET_TYPE_CLIENT &&
-       SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+       memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
       
       /* Route the packet to fastest route for the destination ID */
       void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
@@ -1495,20 +1641,27 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
       silc_free(id);
       goto out;
     }
-    
+  }
+
+  /* Parse the incoming packet type */
+  silc_server_packet_parse_type(server, sock, packet);
+
+  if (server->server_type == SILC_ROUTER) {
     /* Broadcast packet if it is marked as broadcast packet and it is
        originated from router and we are router. */
     if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
-       packet->flags & SILC_PACKET_FLAG_BROADCAST) {
+       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);
     }
   }
 
-  /* Parse the incoming packet type */
-  silc_server_packet_parse_type(server, sock, packet);
-
  out:
-  silc_buffer_clear(sock->inbuf);
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
@@ -1522,23 +1675,23 @@ void silc_server_packet_parse(SilcPacketParserContext *parser_context)
   SilcSocketConnection sock = parser_context->sock;
 
   switch (sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
   case SILC_SOCKET_TYPE_UNKNOWN:
+  case SILC_SOCKET_TYPE_CLIENT:
     /* Parse the packet with timeout */
-    silc_task_register(server->timeout_queue, sock->sock,
-                      silc_server_packet_parse_real,
-                      (void *)parser_context, 0, 100000,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+    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);
     break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     /* Packets from servers are parsed as soon as possible */
-    silc_task_register(server->timeout_queue, sock->sock,
-                      silc_server_packet_parse_real,
-                      (void *)parser_context, 0, 1,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+    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);
     break;
   default:
     return;
@@ -1553,11 +1706,12 @@ void silc_server_packet_parse_type(SilcServer server,
                                   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)
@@ -1573,10 +1727,8 @@ void silc_server_packet_parse_type(SilcServer server,
     SILC_LOG_DEBUG(("Success packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
-    if (sock->protocol) {
-      sock->protocol->execute(server->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    }
+    if (sock->protocol)
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     break;
 
   case SILC_PACKET_FAILURE:
@@ -1595,7 +1747,7 @@ void silc_server_packet_parse_type(SilcServer server,
       f->sock = sock;
       
       /* We will wait 5 seconds to process this failure packet */
-      silc_task_register(server->timeout_queue, sock->sock,
+      silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_failure_callback, (void *)f, 5, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     }
@@ -1632,6 +1784,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;
 
@@ -1685,6 +1838,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;
 
@@ -1705,8 +1859,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1714,13 +1868,10 @@ void silc_server_packet_parse_type(SilcServer server,
       proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 100000);
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
                      "protocol active, packet dropped."));
-
-      /* XXX Trigger KE protocol?? Rekey actually, maybe. */
     }
     break;
 
@@ -1729,26 +1880,39 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
-
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       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);
 
-      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,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      } else {
+       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,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 
                              0, 100000);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -1760,26 +1924,39 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
-
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       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);
 
-      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,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      } else {
+       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,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 
                              0, 100000);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -1796,6 +1973,7 @@ void silc_server_packet_parse_type(SilcServer server,
     SILC_LOG_DEBUG(("Connection authentication request packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
+    silc_server_connection_auth_request(server, sock, packet);
     break;
 
     /*
@@ -1817,8 +1995,7 @@ void silc_server_packet_parse_type(SilcServer server,
       proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 0);
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
                      "protocol active, packet dropped."));
@@ -1894,6 +2071,57 @@ void silc_server_packet_parse_type(SilcServer server,
     silc_server_key_agreement(server, sock, packet);
     break;
 
+  case SILC_PACKET_REKEY:
+    /*
+     * Received re-key packet. The sender wants to regenerate the session
+     * keys.
+     */
+    SILC_LOG_DEBUG(("Re-key packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_rekey(server, sock, packet);
+    break;
+
+  case SILC_PACKET_REKEY_DONE:
+    /*
+     * The re-key is done.
+     */
+    SILC_LOG_DEBUG(("Re-key done packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+
+      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 {
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
+    }
+    break;
+
+  case SILC_PACKET_FTP:
+    /* Ignored */
+    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;
@@ -1904,7 +2132,7 @@ void silc_server_packet_parse_type(SilcServer server,
 /* Creates connection to a remote router. */
 
 void silc_server_create_connection(SilcServer server,
-                                  char *remote_host, unsigned int port)
+                                  char *remote_host, uint32 port)
 {
   SilcServerConnection sconn;
 
@@ -1914,10 +2142,15 @@ void silc_server_create_connection(SilcServer server,
   sconn->remote_host = strdup(remote_host);
   sconn->remote_port = port;
 
-  silc_task_register(server->timeout_queue, 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)
+{
+  silc_socket_free((SilcSocketConnection)context);
 }
 
 /* Closes connection to socket connection */
@@ -1925,19 +2158,42 @@ void silc_server_create_connection(SilcServer server,
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
-  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
+  SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
+                  sock->port,
+                  (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                   sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                   sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                   "Router")));
 
   /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
+  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
 
   /* Unregister all tasks */
-  silc_task_unregister_by_fd(server->io_queue, sock->sock);
-  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
+  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
 
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
   server->sockets[sock->sock] = NULL;
-  silc_socket_free(sock);
+
+  /* If sock->user_data is NULL then we'll check for active protocols
+     here since the silc_server_free_sock_user_data has not been called
+     for this connection. */
+  if (!sock->user_data) {
+    /* If any protocol is active cancel its execution. It will call
+       the final callback which will finalize the disconnection. */
+    if (sock->protocol) {
+      silc_protocol_cancel(sock->protocol, server->schedule);
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute_final(sock->protocol, server->schedule);
+      sock->protocol = NULL;
+      return;
+    }
+  }
+
+  silc_schedule_task_add(server->schedule, 0, 
+                        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 
@@ -1960,13 +2216,6 @@ void silc_server_disconnect_remote(SilcServer server,
 
   SILC_LOG_DEBUG(("Disconnecting remote host"));
 
-  SILC_LOG_INFO(("Disconnecting %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")));
-
   /* 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,  
@@ -2003,26 +2252,13 @@ void silc_server_free_client_data(SilcServer server,
 
   /* 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_send_real(server, sock, TRUE);
-
-    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-    silc_buffer_clear(sock->outbuf);
-  }
+  silc_server_packet_queue_purge(server, sock);
 
   /* Send SIGNOFF notify to routers. */
   if (notify && !server->standalone && server->router)
     silc_server_send_notify_signoff(server, server->router->connection,
                                    server->server_type == SILC_SERVER ?
-                                   FALSE : TRUE, client->id, 
-                                   SILC_ID_CLIENT_LEN, signoff);
+                                   FALSE : TRUE, client->id, signoff);
 
   /* Remove client from all channels */
   if (notify)
@@ -2036,11 +2272,13 @@ void silc_server_free_client_data(SilcServer server,
      into history (for WHOWAS command) for 5 minutes */
   i->server = server;
   i->client = client;
-  silc_task_register(server->timeout_queue, 0, 
-                    silc_server_free_client_data_timeout,
-                    (void *)i, 300, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-  client->data.registered = FALSE;
+  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;
 
   /* Free the client entry and everything in it */
   server->stat.my_clients--;
@@ -2058,7 +2296,7 @@ void silc_server_free_sock_user_data(SilcServer server,
 {
   SILC_LOG_DEBUG(("Start"));
 
-  switch(sock->type) {
+  switch (sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
@@ -2067,150 +2305,106 @@ void silc_server_free_sock_user_data(SilcServer server,
     }
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
-    {
-      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
-
-      /* Send REMOVE_ID packet to routers. */
-      if (!server->standalone && server->router)
-       silc_server_send_notify_server_signoff(server, 
-                                              server->router->connection,
-                                              server->server_type == 
-                                              SILC_SERVER ?
-                                              FALSE : TRUE, user_data->id, 
-                                              SILC_ID_SERVER_LEN);
-
-      /* Then also free all client entries that this server owns as
-        they will become invalid now as well. */
-      silc_server_remove_clients_by_server(server, user_data);
-
-      /* 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;
-    }
-  }
-
-  sock->user_data = NULL;
-}
-
-/* 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'. */
-
-int silc_server_remove_clients_by_server(SilcServer server, 
-                                        SilcServerEntry entry)
-{
-  SilcIDCacheList list = NULL;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcClientEntry client = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (silc_idcache_find_by_id(server->local_list->clients, 
-                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
-
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       
-       if (client->router != entry) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
+    {
+      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+      SilcServerEntry backup_router = NULL;
 
-       /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, TRUE, 
-                                        NULL, TRUE);
-       silc_idlist_del_client(server->local_list, client);
+      /* If this was our primary router connection then we're lost to
+        the outside world. */
+      if (server->router == user_data) {
+       backup_router = silc_server_backup_get(server);
+
+       /* 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);
+         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);
+         }
+
+         /* Mark this connection as replaced */
+         silc_server_backup_replaced_add(server, user_data->id, 
+                                         backup_router);
+       }
+      }
 
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
+      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);
+      } 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);
       }
-    }
-    silc_idcache_list_free(list);
-  }
-  
-  if (silc_idcache_find_by_id(server->global_list->clients, 
-                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
 
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       
-       if (client->router != entry) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
+      /* Free the server entry */
+      silc_server_backup_del(server, user_data);
+      if (user_data->id)
+       silc_server_backup_replaced_del(server, user_data->id);
+      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--;
 
-       /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, TRUE,
-                                        NULL, TRUE);
-       silc_idlist_del_client(server->global_list, client);
+      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);
 
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, time(0) - 300);
+       silc_server_announce_channels(server, time(0) - 300);
       }
+      break;
     }
-    silc_idcache_list_free(list);
-  }
-  
-  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;
+  default:
+    {
+      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
 
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (chl->client->router)
-      return TRUE;
+      silc_idlist_del_data(user_data);
+      silc_free(user_data);
+      break;
+    }
   }
 
-  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;
-
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    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
@@ -2226,6 +2420,7 @@ void silc_server_remove_from_channels(SilcServer server,
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
@@ -2237,16 +2432,18 @@ void silc_server_remove_from_channels(SilcServer server,
 
   /* Remove the client from all channels. The client is removed from
      the channels' user list. */
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+  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_list_del(client->channels, chl);
+    silc_hash_table_del(client->channels, channel);
 
     /* Remove channel if there is no users anymore */
     if (server->server_type == SILC_ROUTER &&
-       silc_list_count(channel->user_list) < 2) {
+       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--;
@@ -2254,19 +2451,20 @@ void silc_server_remove_from_channels(SilcServer server,
     }
 
     /* Remove client from channel's client list */
-    silc_list_del(channel->user_list, chl);
-    silc_free(chl);
-    server->stat.my_chanclients--;
+    silc_hash_table_del(channel->user_list, chl->client);
 
     /* If there is no global users on the channel anymore mark the channel
-       as local channel. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_global(channel))
+       as local channel. Do not check if the removed client is local client. */
+    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)
@@ -2277,6 +2475,26 @@ void silc_server_remove_from_channels(SilcServer server,
                                           signoff_message, signoff_message ?
                                           strlen(signoff_message) : 0);
 
+      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--;
@@ -2293,12 +2511,13 @@ void silc_server_remove_from_channels(SilcServer server,
                                         signoff_message, signoff_message ?
                                         strlen(signoff_message) : 0);
 
-    if (keygen) {
+    if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
       /* Re-generate channel key */
-      silc_server_create_channel_key(server, channel, 0);
+      if (!silc_server_create_channel_key(server, channel, 0))
+       return;
       
       /* Send the channel key to the channel. The key of course is not sent
-        to the client who was removed f rom the channel. */
+        to the client who was removed from the channel. */
       silc_server_send_channel_key(server, client->connection, channel, 
                                   server->server_type == SILC_ROUTER ? 
                                   FALSE : !server->standalone);
@@ -2320,95 +2539,94 @@ int silc_server_remove_from_one_channel(SilcServer server,
                                        SilcClientEntry client,
                                        int notify)
 {
-  SilcChannelEntry ch;
   SilcChannelClientEntry chl;
   SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
 
-  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  /* Get the entry to the channel, if this client is not on the channel
+     then return Ok. */
+  if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl))
+    return TRUE;
 
   /* Remove the client from the channel. The client is removed from
      the channel's user list. */
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
-    if (chl->channel != channel)
-      continue;
-
-    ch = chl->channel;
-
-    /* Remove channel from client's channel list */
-    silc_list_del(client->channels, chl);
-
-    /* Remove channel if there is no users anymore */
-    if (server->server_type == SILC_ROUTER &&
-       silc_list_count(channel->user_list) < 2) {
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       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_list_del(channel->user_list, chl);
-    silc_free(chl);
-    server->stat.my_chanclients--;
-
-    /* If there is no global users on the channel anymore mark the channel
-       as local channel. */
-    if (server->server_type == SILC_SERVER &&
-       !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)) {
-      /* 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);
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      silc_buffer_free(clidp);
-      server->stat.my_channels--;
-      return FALSE;
-    }
+  /* Remove channel from client's channel list */
+  silc_hash_table_del(client->channels, chl->channel);
+
+  /* Remove channel if there is no users anymore */
+  if (server->server_type == SILC_ROUTER &&
+      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);
+    silc_buffer_free(clidp);
+    server->stat.my_channels--;
+    return FALSE;
+  }
 
-    /* Send notify to channel about client leaving the channel */
-    if (notify)
+  /* Remove client from channel's client list */
+  silc_hash_table_del(channel->user_list, chl->client);
+  
+  /* 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_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_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);
-    break;
-  }
-
-  silc_buffer_free(clidp);
-  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'. */
+    
+    silc_buffer_free(clidp);
+    
+    if (channel->rekey)
+      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 
-int silc_server_client_on_channel(SilcClientEntry client,
-                                 SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
+    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);
+      }
+      return FALSE;
+    }
 
-  if (!client || !channel)
+    /* 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--;
     return FALSE;
+  }
 
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END)
-    if (chl->channel == channel)
-      return TRUE;
+  /* Send notify to channel about client leaving the channel */
+  if (notify)
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_LEAVE, 1,
+                                      clidp->data, clidp->len);
 
-  return FALSE;
+  silc_buffer_free(clidp);
+  return TRUE;
 }
 
 /* Timeout callback. This is called if connection is idle or for some
@@ -2420,14 +2638,23 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = server->sockets[fd];
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (!sock)
     return;
 
+  /* 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) {
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    return;
+  }
+
   if (sock->user_data)
     silc_server_free_sock_user_data(server, sock);
 
-  silc_server_disconnect_remote(server, sock, 
-                               "Server closed connection: "
+  silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                "Connection timeout");
 }
 
@@ -2451,9 +2678,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))
@@ -2468,12 +2695,20 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   channel_name = strdup(channel_name);
 
   /* Create the channel */
-  silc_id_create_channel_id(router_id, server->rng, &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;
+  }
   entry = silc_idlist_add_channel(server->local_list, channel_name, 
                                  SILC_CHANNEL_MODE_NONE, channel_id, 
                                  NULL, key, newhmac);
   if (!entry) {
     silc_free(channel_name);
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
     return NULL;
   }
 
@@ -2481,14 +2716,22 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   entry->hmac_name = strdup(hmac);
 
   /* Now create the actual key material */
-  silc_server_create_channel_key(server, entry, 
-                                silc_cipher_get_key_len(key) / 8);
+  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);
+    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_ID_CHANNEL_LEN,
+                                channel_name, entry->id, 
+                                silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
   server->stat.my_channels++;
@@ -2513,9 +2756,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))
@@ -2539,14 +2782,18 @@ silc_server_create_new_channel_with_id(SilcServer server,
   }
 
   /* Now create the actual key material */
-  silc_server_create_channel_key(server, entry, 
-                                silc_cipher_get_key_len(key) / 8);
+  if (!silc_server_create_channel_key(server, entry, 
+                                     silc_cipher_get_key_len(key) / 8)) {
+    silc_free(channel_name);
+    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_ID_CHANNEL_LEN,
+                                channel_name, entry->id, 
+                                silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
   server->stat.my_channels++;
@@ -2554,23 +2801,43 @@ silc_server_create_new_channel_with_id(SilcServer server,
   return entry;
 }
 
+/* Channel's key re-key timeout callback. */
+
+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);
+}
+
 /* 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. */
 
-void silc_server_create_channel_key(SilcServer server, 
+bool silc_server_create_channel_key(SilcServer server, 
                                    SilcChannelEntry channel,
-                                   unsigned int key_len)
+                                   uint32 key_len)
 {
   int i;
   unsigned char channel_key[32], hash[32];
-  unsigned int len;
+  uint32 len;
 
   SILC_LOG_DEBUG(("Generating channel key"));
 
+  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
+    return TRUE;
+  }
+
   if (!channel->channel_key)
-    if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
-      return;
+    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key))
+      return FALSE;
 
   if (key_len)
     len = key_len;
@@ -2599,10 +2866,29 @@ void silc_server_create_channel_key(SilcServer server,
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+    silc_hmac_alloc(SILC_DEFAULT_HMAC, 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));
   memset(hash, 0, sizeof(hash));
+
+  if (server->server_type == SILC_ROUTER) {
+    if (!channel->rekey)
+      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    channel->rekey->context = (void *)server;
+    channel->rekey->channel = channel;
+    channel->rekey->key_len = key_len;
+    if (channel->rekey->task)
+      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, 3600, 0,
+                            SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+  }
+
+  return TRUE;
 }
 
 /* Saves the channel key found in the encoded `key_payload' buffer. This 
@@ -2616,9 +2902,11 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
   unsigned char *tmp, hash[32];
-  unsigned int tmp_len;
+  uint32 tmp_len;
   char *cipher;
 
+  SILC_LOG_DEBUG(("Start"));
+
   /* Decode channel key payload */
   payload = silc_channel_key_payload_parse(key_payload);
   if (!payload) {
@@ -2685,16 +2973,31 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+    silc_hmac_alloc(SILC_DEFAULT_HMAC, 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));
 
   memset(hash, 0, sizeof(hash));
   memset(tmp, 0, tmp_len);
 
+  if (server->server_type == SILC_ROUTER) {
+    if (!channel->rekey)
+      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    channel->rekey->context = (void *)server;
+    channel->rekey->channel = channel;
+    if (channel->rekey->task)
+      silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+    channel->rekey->task = 
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_channel_key_rekey,
+                            (void *)channel->rekey, 3600, 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);
 
@@ -2721,8 +3024,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;
@@ -2730,12 +3035,21 @@ static void silc_server_announce_get_servers(SilcServer server,
   SilcBuffer idp;
 
   /* Go through all clients in the list */
-  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
-                             SILC_ID_SERVER, &list)) {
+  if (silc_idcache_get_all(id_list->servers, &list)) {
     if (silc_idcache_list_first(list, &id_cache)) {
       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, 
@@ -2757,19 +3071,27 @@ static void silc_server_announce_get_servers(SilcServer server,
 }
 
 /* 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)
 {
   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, server->router,
+                                  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, server->router,
+                                    server->global_list, &servers,
+                                    creation_time);
 
   if (servers) {
     silc_buffer_push(servers, servers->data - servers->head);
@@ -2789,7 +3111,8 @@ void silc_server_announce_servers(SilcServer server)
 
 static void silc_server_announce_get_clients(SilcServer server,
                                             SilcIDList id_list,
-                                            SilcBuffer *clients)
+                                            SilcBuffer *clients,
+                                            unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -2797,12 +3120,17 @@ static void silc_server_announce_get_clients(SilcServer server,
   SilcBuffer idp;
 
   /* Go through all clients in the list */
-  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
-                             SILC_ID_CLIENT, &list)) {
+  if (silc_idcache_get_all(id_list->clients, &list)) {
     if (silc_idcache_list_first(list, &id_cache)) {
       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, 
@@ -2824,9 +3152,12 @@ 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)
 {
   SilcBuffer clients = NULL;
 
@@ -2834,12 +3165,12 @@ void silc_server_announce_clients(SilcServer server)
 
   /* Get clients in local list */
   silc_server_announce_get_clients(server, server->local_list,
-                                  &clients);
+                                  &clients, 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, creation_time);
 
   if (clients) {
     silc_buffer_push(clients, clients->data - clients->head);
@@ -2855,90 +3186,158 @@ void silc_server_announce_clients(SilcServer server)
 }
 
 static SilcBuffer 
-silc_server_announce_encode_join(unsigned int argc, ...)
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
 {
   va_list ap;
+  SilcBuffer p;
 
   va_start(ap, argc);
-  return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
+  p = silc_notify_payload_encode(notify, argc, ap);
+  va_end(ap);
+  return p;
+}
+
+/* Returns assembled packets for channel users of the `channel'. */
+
+void silc_server_announce_get_channel_users(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *channel_users,
+                                           SilcBuffer *channel_users_modes)
+{
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer chidp, clidp;
+  SilcBuffer tmp;
+  int len;
+  unsigned char mode[4];
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Now find all users on the channel */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+
+    /* JOIN Notify */
+    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)->truelen + len : len));
+    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, 
+                                            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)->truelen + len : len));
+    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_buffer_free(chidp);
 }
 
 /* Returns assembled packets for all channels and users on those channels
    from the given ID List. The packets are in the form dictated by the
    New Channel and New Channel User payloads. */
 
-static void silc_server_announce_get_channels(SilcServer server,
-                                             SilcIDList id_list,
-                                             SilcBuffer *channels,
-                                             SilcBuffer *channel_users)
+void silc_server_announce_get_channels(SilcServer server,
+                                      SilcIDList id_list,
+                                      SilcBuffer *channels,
+                                      SilcBuffer *channel_users,
+                                      SilcBuffer **channel_users_modes,
+                                      uint32 *channel_users_modes_c,
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcBuffer chidp;
   unsigned char *cid;
-  unsigned short name_len;
+  uint32 id_len;
+  uint16 name_len;
   int len;
+  int i = *channel_users_modes_c;
+  bool announce;
+
+  SILC_LOG_DEBUG(("Start"));
 
   /* Go through all channels in the list */
-  if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY, 
-                             SILC_ID_CHANNEL, &list)) {
+  if (silc_idcache_get_all(id_list->channels, &list)) {
     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 + SILC_ID_CHANNEL_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(SILC_ID_CHANNEL_LEN),
-                          SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
-                          SILC_STR_UI_INT(0),
-                          SILC_STR_END);
-       silc_buffer_pull(*channels, len);
-
-       /* Now find all users on the channel */
-       chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-       silc_list_start(channel->user_list);
-       while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-         SilcBuffer clidp;
-         SilcBuffer tmp;
-
-         clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
-
-         tmp = silc_server_announce_encode_join(2, clidp->data, clidp->len,
-                                                chidp->data, chidp->len);
-         len = tmp->len;
-         *channel_users = 
-           silc_buffer_realloc(*channel_users, 
-                               (*channel_users ? 
-                                (*channel_users)->truelen + len : len));
-         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(clidp);
-         silc_buffer_free(tmp);
+       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);
        }
-       silc_buffer_free(chidp);
 
-       silc_free(cid);
+       *channel_users_modes = silc_realloc(*channel_users_modes,
+                                           sizeof(**channel_users_modes) * 
+                                           (i + 1));
+       (*channel_users_modes)[i] = NULL;
+       *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_ids)[i] = channel->id;
+       i++;
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
       }
+
+      *channel_users_modes_c += i;
     }
 
     silc_idcache_list_free(list);
@@ -2947,21 +3346,34 @@ static 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. */
+   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)
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time)
 {
   SilcBuffer channels = NULL, channel_users = NULL;
+  SilcBuffer *channel_users_modes = NULL;
+  uint32 channel_users_modes_c = 0;
+  SilcChannelID **channel_ids = NULL;
 
   SILC_LOG_DEBUG(("Announcing channels and channel users"));
 
   /* Get channels and channel users in local list */
   silc_server_announce_get_channels(server, server->local_list,
-                                   &channels, &channel_users);
+                                   &channels, &channel_users,
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
   silc_server_announce_get_channels(server, server->global_list,
-                                   &channels, &channel_users);
+                                   &channels, &channel_users,
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_ids, creation_time);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
@@ -2989,6 +3401,27 @@ void silc_server_announce_channels(SilcServer server)
 
     silc_buffer_free(channel_users);
   }
+
+  if (channel_users_modes) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      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, 
+                      channel_users_modes[i]->len);
+      silc_server_packet_send_dest(server, server->router->connection,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  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);
+  }
 }
 
 /* Failure timeout callback. If this is called then we will immediately
@@ -3004,8 +3437,7 @@ SILC_TASK_CALLBACK(silc_server_failure_callback)
 
   if (f->sock->protocol) {
     f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-    f->sock->protocol->execute(f->server->timeout_queue, 0,
-                              f->sock->protocol, f->sock->sock, 0, 0);
+    silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
   }
 
   silc_free(f);
@@ -3017,22 +3449,27 @@ void silc_server_get_users_on_channel(SilcServer server,
                                      SilcChannelEntry channel,
                                      SilcBuffer *user_list,
                                      SilcBuffer *mode_list,
-                                     unsigned int *user_count)
+                                     uint32 *user_count)
 {
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   SilcBuffer idp;
-  unsigned int list_count = 0;
+  uint32 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);
 
-  client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) * 
-                                    silc_list_count(channel->user_list));
-  client_mode_list = silc_buffer_alloc(4 * 
-                                      silc_list_count(channel->user_list));
+  client_id_list = silc_buffer_alloc(len);
+  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));
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     /* Client ID */
     idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
     silc_buffer_put(client_id_list, idp->data, idp->len);
@@ -3063,16 +3500,13 @@ void silc_server_save_users_on_channel(SilcServer server,
                                       SilcClientID *noadd,
                                       SilcBuffer user_list,
                                       SilcBuffer mode_list,
-                                      unsigned int user_count)
+                                      uint32 user_count)
 {
   int i;
 
-  /* 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++) {
-    unsigned short idp_len;
-    unsigned int mode;
+    uint16 idp_len;
+    uint32 mode;
     SilcClientID *client_id;
     SilcClientEntry client;
 
@@ -3088,17 +3522,18 @@ void silc_server_save_users_on_channel(SilcServer server,
     SILC_GET32_MSB(mode, mode_list->data);
     silc_buffer_pull(mode_list, 4);
 
-    if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+    if (noadd && SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
       silc_free(client_id);
       continue;
     }
     
     /* Check if we have this client cached already. */
     client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                          NULL);
+                                          server->server_type, NULL);
     if (!client)
       client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, NULL);
+                                            client_id, server->server_type,
+                                            NULL);
     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. */
@@ -3110,16 +3545,16 @@ 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 
         global. */
-      client = silc_idlist_add_client(server->global_list, NULL, 0, NULL, 
-                                     NULL, 
+      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
                                      silc_id_dup(client_id, SILC_ID_CLIENT), 
                                      sock->user_data, NULL);
       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;
     }
 
     silc_free(client_id);
@@ -3130,20 +3565,20 @@ void silc_server_save_users_on_channel(SilcServer server,
       chl->client = client;
       chl->mode = mode;
       chl->channel = channel;
-      silc_list_add(channel->user_list, chl);
-      silc_list_add(client->channels, chl);
+      silc_hash_table_add(channel->user_list, chl->client, chl);
+      silc_hash_table_add(client->channels, chl->channel, chl);
     }
   }
 }
 
-/* Lookups route to the client indicated by `id' client ID. The connection
+/* Lookups route to the client indicated by the `id_data'. The connection
    object and internal data object is returned. Returns NULL if route
    could not be found to the client. If the `client_id' is specified then
    it is used and the `id_data' is ignored. */
 
 SilcSocketConnection silc_server_get_client_route(SilcServer server,
                                                  unsigned char *id_data,
-                                                 unsigned int id_len,
+                                                 uint32 id_len,
                                                  SilcClientID *client_id,
                                                  SilcIDListData *idata)
 {
@@ -3165,33 +3600,33 @@ 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 && 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) {
-      /* We are of course in this case the client's router thus the real
-        "router" of the client is the server who owns the client. Thus
-        we will send the packet to that server. */
-      *idata = (SilcIDListData)client->router;
+      /* We are of course in this case the client's router thus the route
+        to the client is the server who owns the client. So, we will send
+        the packet to that server. */
+      if (idata)
+       *idata = (SilcIDListData)client->router;
       return client->router->connection;
     }
 
     /* Seems that client really is directly connected to us */
-    *idata = (SilcIDListData)client;
+    if (idata)
+      *idata = (SilcIDListData)client;
     return client->connection;
   }
 
   /* 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);
-    *idata = (SilcIDListData)server->router;
+    if (idata)
+      *idata = (SilcIDListData)server->router;
     return server->router->connection;
   }
 
@@ -3199,14 +3634,16 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
      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;
 
       dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
 
       silc_free(id);
-      *idata = (SilcIDListData)dst_sock->user_data;
+      if (idata)
+       *idata = (SilcIDListData)dst_sock->user_data;
       return dst_sock;
     }
   }
@@ -3224,21 +3661,25 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   SilcBuffer buffer = NULL;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   unsigned char *cid;
-  unsigned short name_len;
+  uint32 id_len;
+  uint16 name_len;
   int len;
 
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+  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 + SILC_ID_CHANNEL_LEN + 4;
+    len = 4 + name_len + id_len + 4;
     buffer = silc_buffer_realloc(buffer, 
                                 (buffer ? (buffer)->truelen + len : len));
     silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
@@ -3246,8 +3687,8 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
                       SILC_STR_UI_SHORT(name_len),
                       SILC_STR_UI_XNSTRING(channel->channel_name, 
                                            name_len),
-                      SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                      SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                      SILC_STR_UI_SHORT(id_len),
+                      SILC_STR_UI_XNSTRING(cid, id_len),
                       SILC_STR_UI_INT(chl->mode), /* Client's mode */
                       SILC_STR_END);
     silc_buffer_pull(buffer, len);
@@ -3268,10 +3709,11 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
 {
   SilcClientEntry client;
 
-  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+  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_id, TRUE, NULL);
     if (!client && server->server_type == SILC_ROUTER)
       return NULL;
   }
@@ -3282,9 +3724,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,
@@ -3292,7 +3738,87 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
                            buffer->data, buffer->len, FALSE);
     silc_buffer_free(idp);
     silc_buffer_free(buffer);
+    return NULL;
   }
 
   return client;
 }
+
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_callback)
+{
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcServer server = (SilcServer)idata->rekey->context;
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->sock = sock;
+  proto_ctx->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, 
+                     &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);
+}
+
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx =
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+  SilcSocketConnection sock = ctx->sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  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;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_free(ctx);
+    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;
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_free(ctx);
+}