Fixed server/backup router reconnecting
[silc.git] / apps / silcd / server.c
index e6a896000fe0f0c18b6fa4207414853a8b4528d0..bb5b3b279e819a72d240fa388f5b8fb7610d0e0a 100644 (file)
 
   server.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2008 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
 */
-/*
- * This is the actual SILC server than handles everything relating to
- * servicing the SILC connections. This is also a SILC router as a router 
- * is also normal server.
- */
-/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 
-/* Static prototypes */
-SILC_TASK_CALLBACK(silc_server_connect_router);
-SILC_TASK_CALLBACK(silc_server_connect_to_router);
-SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
-SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
-SILC_TASK_CALLBACK(silc_server_accept_new_connection);
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_second);
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
-SILC_TASK_CALLBACK(silc_server_packet_process);
-SILC_TASK_CALLBACK(silc_server_packet_parse_real);
-SILC_TASK_CALLBACK(silc_server_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
-   the server. The new allocated server object is returned to the new_server
-   argument. */
+/************************* Types and definitions ****************************/
 
-int silc_server_alloc(SilcServer *new_server)
+SILC_TASK_CALLBACK(silc_server_get_stats);
+SILC_TASK_CALLBACK(silc_server_connect_router);
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry);
+SILC_TASK_CALLBACK(silc_server_do_rekey);
+SILC_TASK_CALLBACK(silc_server_purge_expired_clients);
+static void silc_server_accept_new_connection(SilcNetStatus status,
+                                             SilcStream stream,
+                                             void *context);
+static void silc_server_packet_parse_type(SilcServer server,
+                                         SilcPacketStream sock,
+                                         SilcPacket packet);
+static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
+                             SilcPacket packet);
+
+
+/************************ Static utility functions **************************/
+
+/* SKE public key verification callback */
+
+static void
+silc_server_verify_key(SilcSKE ske,
+                      SilcPublicKey public_key,
+                      void *context,
+                      SilcSKEVerifyCbCompletion completion,
+                      void *completion_context)
 {
-  SilcServer server;
-
-  SILC_LOG_DEBUG(("Allocating new server object"));
+  SilcPacketStream sock = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sock);
 
-  server = silc_calloc(1, sizeof(*server));
-  server->server_type = SILC_SERVER;
-  server->standalone = TRUE;
-  server->local_list = silc_calloc(1, sizeof(*server->local_list));
-  server->global_list = silc_calloc(1, sizeof(*server->global_list));
-  server->pending_commands = silc_dlist_init();
-#ifdef SILC_SIM
-  server->sim = silc_dlist_init();
-#endif
+  SILC_LOG_DEBUG(("Verifying public key"));
 
-  *new_server = server;
+  if (silc_pkcs_get_type(public_key) != SILC_SKE_PK_TYPE_SILC) {
+    SILC_LOG_WARNING(("We don't support %s (%s) port %d public key type %d",
+                     entry->hostname, entry->ip, entry->port,
+                     silc_pkcs_get_type(public_key)));
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+              completion_context);
+    return;
+  }
 
-  return TRUE;
+  /* We accept all keys without explicit verification */
+  completion(ske, SILC_SKE_STATUS_OK, completion_context);
 }
 
-/* Free's the SILC server object. This is called at the very end before
-   the program ends. */
-
-void silc_server_free(SilcServer server)
-{
-  if (server) {
-#ifdef SILC_SIM
-    SilcSimContext *sim;
-#endif
-
-    if (server->local_list)
-      silc_free(server->local_list);
-    if (server->global_list)
-      silc_free(server->global_list);
-    if (server->rng)
-      silc_rng_free(server->rng);
-
-#ifdef SILC_SIM
-    while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
-      silc_dlist_del(server->sim, sim);
-      silc_sim_free(sim);
-    }
-    silc_dlist_uninit(server->sim);
-#endif
-
-    if (server->params)
-      silc_free(server->params);
 
-    if (server->pending_commands)
-      silc_dlist_uninit(server->pending_commands);
-
-    silc_free(server);
-  }
-}
+/************************ Packet engine callbacks ***************************/
 
-/* Initializes the entire SILC server. This is called always before running
-   the server. This is called only once at the initialization of the program.
-   This binds the server to its listenning port. After this function returns 
-   one should call silc_server_run to start the server. This returns TRUE 
-   when everything is ok to run the server. Configuration file must be
-   read and parsed before calling this. */
+/* Packet engine callback to receive a packet */
 
-int silc_server_init(SilcServer server)
+static SilcBool silc_server_packet_receive(SilcPacketEngine engine,
+                                          SilcPacketStream stream,
+                                          SilcPacket packet,
+                                          void *callback_context,
+                                          void *stream_context)
 {
-  int *sock = NULL, sock_count = 0, i;
-  SilcServerID *id;
-  SilcServerEntry id_entry;
-  SilcIDListPurge purge;
+  SilcServer server = callback_context;
+  SilcIDListData idata = stream_context;
 
-  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"));
+  if (!idata)
     return FALSE;
-  }
-  server->public_key = server->config->server_keys->public_key;
-  server->private_key = server->config->server_keys->private_key;
-
-  /* XXX After server is made as Silc Server Library this can be given
-     as argument, for now this is hard coded */
-  server->params = silc_calloc(1, sizeof(*server->params));
-  server->params->retry_count = SILC_SERVER_RETRY_COUNT;
-  server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN;
-  server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
-  server->params->retry_keep_trying = FALSE;
-  server->params->protocol_timeout = 60;
-  server->params->require_reverse_mapping = FALSE;
-
-  /* Set log files where log message should be saved. */
-  server->config->server = server;
-  silc_server_config_setlogfiles(server->config);
-  /* 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);
-
-  /* Initialize random number generator for the server. */
-  server->rng = silc_rng_alloc();
-  silc_rng_init(server->rng);
-  silc_rng_global_init(server->rng);
 
-  /* Initialize hash functions for server to use */
-  silc_hash_alloc("md5", &server->md5hash);
-  silc_hash_alloc("sha1", &server->sha1hash);
+  /* Packets we do not handle */
+  switch (packet->type) {
+  case SILC_PACKET_HEARTBEAT:
+  case SILC_PACKET_SUCCESS:
+  case SILC_PACKET_FAILURE:
+  case SILC_PACKET_REJECT:
+  case SILC_PACKET_KEY_EXCHANGE:
+  case SILC_PACKET_KEY_EXCHANGE_1:
+  case SILC_PACKET_KEY_EXCHANGE_2:
+  case SILC_PACKET_REKEY_DONE:
+  case SILC_PACKET_CONNECTION_AUTH:
+    return FALSE;
+    break;
+  }
 
-  /* Initialize none cipher */
-  silc_cipher_alloc("none", &server->none_cipher);
+  /* Only specific packets can come without source ID present. */
+  if ((!packet->src_id ||
+       !(idata->status & SILC_IDLIST_STATUS_REGISTERED)) &&
+      packet->type != SILC_PACKET_NEW_CLIENT &&
+      packet->type != SILC_PACKET_NEW_SERVER &&
+      packet->type != SILC_PACKET_RESUME_CLIENT &&
+      packet->type != SILC_PACKET_CONNECTION_AUTH_REQUEST &&
+      packet->type != SILC_PACKET_DISCONNECT)
+    return FALSE;
 
-  /* Create a listening server. Note that our server can listen on
-     multiple ports. All listeners are created here and now. */
-  /* XXX Still check this whether to use server_info or listen_port. */
-  sock_count = 0;
-  while(server->config->listen_port) {
-    int tmp;
+  /* NEW_CLIENT and NEW_SERVER are accepted only without source ID and
+     for unregistered connection. */
+  if (packet->src_id && (packet->type == SILC_PACKET_NEW_CLIENT ||
+                        packet->type == SILC_PACKET_NEW_SERVER) &&
+      (idata->status & SILC_IDLIST_STATUS_REGISTERED))
+    return FALSE;
 
-    tmp = silc_net_create_server(server->config->listen_port->port,
-                                server->config->listen_port->host);
-    if (tmp < 0)
-      goto err0;
+  /* Ignore packets from disabled connection */
+  if (idata->status & SILC_IDLIST_STATUS_DISABLED &&
+      packet->type != SILC_PACKET_HEARTBEAT &&
+      packet->type != SILC_PACKET_RESUME_ROUTER &&
+      packet->type != SILC_PACKET_REKEY)
+    return FALSE;
 
-    sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
-    sock[sock_count] = tmp;
-    server->config->listen_port = server->config->listen_port->next;
-    sock_count++;
+  /* Check that the the current client ID is same as in the client's packet. */
+  if (idata->conn_type == SILC_CONN_CLIENT) {
+    SilcClientEntry client = (SilcClientEntry)silc_packet_get_context(stream);
+    SilcClientID client_id;
+
+    if (client->id && packet->src_id &&
+       silc_id_str2id(packet->src_id, packet->src_id_len,
+                      packet->src_id_type, &client_id, sizeof(client_id))) {
+      if (!SILC_ID_CLIENT_COMPARE(client->id, &client_id)) {
+       SILC_LOG_DEBUG(("Packet source is not same as sender, packet %s",
+                       silc_get_packet_name(packet->type)));
+       return FALSE;
+      }
+    }
   }
 
-  /* 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);
-
-  /* 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);
-
-  /* Allocate the entire socket list that is used in server. Eventually 
-     all connections will have entry in this table (it is a table of 
-     pointers to the actual object that is allocated individually 
-     later). */
-  server->sockets = silc_calloc(SILC_SERVER_MAX_CONNECTIONS,
-                               sizeof(*server->sockets));
-
-  for (i = 0; i < sock_count; i++) {
-    SilcSocketConnection newsocket = NULL;
-
-    /* Set socket to non-blocking mode */
-    silc_net_set_socket_nonblock(sock[i]);
-    server->sock = sock[i];
-    
-    /* Create a Server ID for the server. */
-    silc_id_create_server_id(sock[i], server->rng, &id);
-    if (!id) {
-      goto err0;
-    }
-    
-    server->id = id;
-    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
-    server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
-    server->id_type = SILC_ID_SERVER;
-    server->server_name = server->config->server_info->server_name;
-
-    /* Add ourselves to the server list. We don't have a router yet 
-       beacuse we haven't established a route yet. It will be done later. 
-       For now, NULL is sent as router. This allocates new entry to
-       the ID list. */
-    id_entry = 
-      silc_idlist_add_server(server->local_list,
-                            server->config->server_info->server_name,
-                            server->server_type, server->id, NULL, NULL);
-    if (!id_entry) {
-      SILC_LOG_ERROR(("Could not add ourselves to cache"));
-      goto err0;
-    }
-    
-    /* Add ourselves also to the socket table. The entry allocated above
-       is sent as argument for fast referencing in the future. */
-    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, 
-                     &newsocket);
-
-    server->sockets[sock[i]] = newsocket;
-    
-    /* Perform name and address lookups to resolve the listenning address
-       and port. */
-    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
-                                    &newsocket->ip)) {
-      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-         !newsocket->ip) {
-       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
-                       newsocket->hostname ? newsocket->hostname :
-                       newsocket->ip ? newsocket->ip : ""));
-       server->stat.conn_failures++;
-       goto err0;
+  if (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 &&
+       !(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
+       packet->dst_id_type == SILC_ID_SERVER &&
+       idata->conn_type != SILC_CONN_CLIENT &&
+       memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
+      SilcPacketStream conn;
+      SilcServerID server_id;
+
+      silc_id_str2id(packet->dst_id, packet->dst_id_len, packet->dst_id_type,
+                    &server_id, sizeof(server_id));
+
+      conn = silc_server_route_get(server, &server_id, SILC_ID_SERVER);
+      if (!conn) {
+       SILC_LOG_WARNING(("Packet to unknown server ID %s, dropped (no route)",
+                         silc_id_render(&server_id, SILC_ID_SERVER)));
+       return FALSE;
       }
-      if (!newsocket->hostname)
-       newsocket->hostname = strdup(newsocket->ip);
-    }
-    newsocket->port = silc_net_get_local_port(sock[i]);
 
-    /* Put the allocated socket pointer also to the entry allocated above 
-       for fast back-referencing to the socket list. */
-    id_entry->connection = (void *)server->sockets[sock[i]];
-    server->id_entry = id_entry;
+      silc_server_packet_route(server, conn, packet);
+      silc_packet_free(packet);
+      return TRUE;
+    }
   }
 
-  /* 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;
+  /* Broadcast packet if it is marked as broadcast packet and it is
+     originated from router and we are router. */
+  if (server->server_type == SILC_ROUTER &&
+      idata->conn_type == SILC_CONN_ROUTER &&
+      packet->flags & SILC_PACKET_FLAG_BROADCAST) {
+    /* Broadcast to our primary route */
+    silc_server_packet_broadcast(server, SILC_PRIMARY_ROUTE(server), packet);
+
+    /* If we have backup routers then we need to feed all broadcast
+       data to those servers. */
+    silc_server_backup_broadcast(server, stream, packet);
   }
-  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);
-  
-  /* Add the first task to the queue. 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);
-
-  /* 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);
-  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)
-    server->server_type = SILC_ROUTER;
+  /* Process packet */
+  silc_server_packet_parse_type(server, stream, packet);
 
-  /* Register the ID Cache purge task. This periodically purges the ID cache
-     and removes the expired cache entries. */
-
-  /* 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);
-
-  /* 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);
+  return TRUE;
+}
 
-  SILC_LOG_DEBUG(("Server initialized"));
+/* Packet engine callback to indicate end of stream */
 
-  /* We are done here, return succesfully */
-  return TRUE;
+static void silc_server_packet_eos(SilcPacketEngine engine,
+                                  SilcPacketStream stream,
+                                  void *callback_context,
+                                  void *stream_context)
+{
+  SilcServer server = callback_context;
+  SilcIDListData idata = silc_packet_get_context(stream);
 
-  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]);
+  SILC_LOG_DEBUG(("End of stream received, sock %p", stream));
 
-  return FALSE;
-}
+  if (!idata)
+    return;
 
-/* Fork server to background and set gid+uid to non-root.
-   Silcd will not run as root, so trying to set either user or group to
-   root will cause silcd to exit. */
-
-void silc_server_daemonise(SilcServer server)
-{
-  /* Are we executing silcd as root or a regular user? */
-  if (geteuid()==0) {
-    
-    struct passwd *pw;
-    struct group *gr;
-    char *user, *group;
-    
-    if (!server->config->identity || !server->config->identity->user || 
-       !server->config->identity->group) {
-      fprintf(stderr, "Error:"
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the [Identity] configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    }
-    
-    /* Get the values given for user and group in configuration file */
-    user=server->config->identity->user;
-    group=server->config->identity->group;
-    
-    /* Check whether the user/group information is text */ 
-    if (atoi(user)!=0 || atoi(group)!=0) {
-      SILC_LOG_DEBUG(("Invalid user and/or group information"));
-      SILC_LOG_DEBUG(("User and/or group given as number"));
-      fprintf(stderr, "Invalid user and/or group information\n");
-      fprintf(stderr, "Please assign them as names, not numbers\n");
-      exit(1);
-    }
-    
-    /* Catch the nasty incident of string "0" returning 0 from atoi */
-    if (strcmp("0", user)==0 || strcmp("0", group)==0) {
-      SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
-      fprintf(stderr, "User and/or group configured to 0. Exiting\n");
-      exit(1);
-    }
-    
-    pw=getpwnam(user);
-    gr=getgrnam(group);
-
-    if (!pw) {
-      fprintf(stderr, "No such user %s found\n", user);
-      exit(1);
-    }
-
-    if (!gr) {
-      fprintf(stderr, "No such group %s found\n", group);
-      exit(1);
-    }
-    
-    /* Check whether user and/or group is set to root. If yes, exit
-       immediately. Otherwise, setgid and setuid server to user.group */
-    if (gr->gr_gid==0 || pw->pw_uid==0) {
-      fprintf(stderr, "Error:"
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the [Identity] configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    } else {
-      /* Fork server to background, making it a daemon */
-      if (fork()) {
-        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
-        SILC_LOG_DEBUG(("Forking SILC server to background"));
-        exit(0);
-      } 
-      setsid();
-      
-      SILC_LOG_DEBUG(("Changing to group %s", group));
-      if(setgid(gr->gr_gid)==0) {
-        SILC_LOG_DEBUG(("Setgid to %s", group));
-      } else {
-        SILC_LOG_DEBUG(("Setgid to %s failed", group));
-        fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
-                group);
-        exit(1);
-      }
-      SILC_LOG_DEBUG(("Changing to user nobody"));
-      if(setuid(pw->pw_uid)==0) {
-        SILC_LOG_DEBUG(("Setuid to %s", user));
-      } else {
-        SILC_LOG_DEBUG(("Setuid to %s failed", user));
-        fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
-                user);
-        exit(1);
-      }
-    }
+  if (server->router_conn && server->router_conn->sock == stream &&
+      !server->router && server->standalone) {
+    silc_server_create_connections(server);
+    silc_server_free_sock_user_data(server, stream, NULL);
   } else {
-    /* Fork server to background, making it a daemon */
-    if (fork()) {
-      SILC_LOG_DEBUG(("Server started as user")); 
-      SILC_LOG_DEBUG(("Forking SILC server to background"));
-      exit(0);
+    /* If backup disconnected then mark that resuming will not be allowed */
+     if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         idata->conn_type == SILC_CONN_SERVER) {
+      SilcServerEntry server_entry = (SilcServerEntry)idata;
+      if (server_entry->server_type == SILC_BACKUP_ROUTER)
+        server->backup_closed = TRUE;
     }
-    setsid();
+
+    silc_server_free_sock_user_data(server, stream, NULL);
   }
-}
 
-/* 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_server_close_connection(server, stream);
+}
 
-void silc_server_stop(SilcServer server)
+SILC_TASK_CALLBACK(silc_server_packet_error_timeout)
 {
-  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();
+  SilcServer server = app_context;
+  SilcPacketStream stream = context;
+  SilcIDListData idata = silc_packet_get_context(stream);
 
-  silc_server_protocols_unregister();
-
-  SILC_LOG_DEBUG(("Server stopped"));
-}
+  if (!idata)
+    return;
 
-/* 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. */
+  if (server->router_conn && server->router_conn->sock == stream &&
+      !server->router && server->standalone) {
+    silc_server_create_connections(server);
+  } else {
+    /* If backup disconnected then mark that resuming will not be allowed */
+     if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         idata->conn_type == SILC_CONN_SERVER) {
+      SilcServerEntry server_entry = (SilcServerEntry)idata;
+      if (server_entry->server_type == SILC_BACKUP_ROUTER)
+        server->backup_closed = TRUE;
+    }
 
-void silc_server_run(SilcServer server)
-{
-  SILC_LOG_DEBUG(("Running server"));
+    silc_server_free_sock_user_data(server, stream, NULL);
+  }
 
-  /* Start the scheduler, the heart of the SILC server. When this returns
-     the program will be terminated. */
-  silc_schedule();
+  silc_server_close_connection(server, stream);
 }
 
-/* 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. */
+/* Packet engine callback to indicate error */
 
-SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+static void silc_server_packet_error(SilcPacketEngine engine,
+                                    SilcPacketStream stream,
+                                    SilcPacketError error,
+                                    void *callback_context,
+                                    void *stream_context)
 {
-  SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
-
-  SILC_LOG_INFO(("Retrying connecting to a router"));
+  SilcServer server = callback_context;
+  SilcIDListData idata = silc_packet_get_context(stream);
+  SilcStream sock = silc_packet_stream_get_stream(stream);
+  const char *ip;
+  SilcUInt16 port;
 
-  /* 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_LOG_DEBUG(("Packet error, sock %p", stream));
 
-  /* 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"));
+  if (!idata || !sock)
     return;
-  }
-
-  /* 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);
-}
-
-/* Generic routine to use connect to a router. */
 
-SILC_TASK_CALLBACK(silc_server_connect_router)
-{    
-  SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
-  SilcSocketConnection newsocket;
-  SilcProtocol protocol;
-  SilcServerKEInternalContext *proto_ctx;
-  int sock;
+  if (!silc_socket_stream_get_info(sock, NULL, NULL, &ip, &port))
+    return;
 
-  SILC_LOG_INFO(("Connecting to the router %s on port %d", 
-                sconn->remote_host, sconn->remote_port));
+  SILC_LOG_ERROR(("Connection %s:%d [%s]: %s", ip, port,
+                 SILC_CONNTYPE_STRING(idata->conn_type),
+                 silc_packet_error_string(error)));
 
-  /* 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);
+  if (!silc_packet_stream_is_valid(stream))
     return;
-  }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* Create socket connection for the connection. Even though we
-     know that we are connecting to a router we will mark the socket
-     to be unknown connection until we have executed authentication
-     protocol. */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-  server->sockets[sock] = newsocket;
-  newsocket->hostname = strdup(sconn->remote_host);
-  newsocket->ip = strdup(sconn->remote_host);
-  newsocket->port = sconn->remote_port;
-  sconn->sock = newsocket;
-
-  /* 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->context = (void *)sconn;
-  proto_ctx->sock = newsocket;
-  proto_ctx->rng = server->rng;
-  proto_ctx->responder = FALSE;
-      
-  /* Perform key exchange protocol. silc_server_connect_to_router_second
-     will be called after the protocol is finished. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &protocol, proto_ctx,
-                     silc_server_connect_to_router_second);
-  newsocket->protocol = protocol;
-      
-  /* Register a timeout task that will be executed if the protocol
-     is not executed within set limit. */
-  proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock, 
-                      silc_server_timeout_remote,
-                      server, server->params->protocol_timeout,
-                      server->params->protocol_timeout_usec,
-                      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. */
-  context = (void *)server;
-  SILC_REGISTER_CONNECTION_FOR_IO(sock);
-  
-  /* Run the protocol */
-  protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_packet_error_timeout,
+                                stream, 0, 0);
 }
-  
-/* This function connects to our primary router or if we are a router this
-   establishes all our primary routes. This is called at the start of the
-   server to do authentication and key exchange with our router - called
-   from schedule. */
 
-SILC_TASK_CALLBACK(silc_server_connect_to_router)
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_server_stream_cbs =
 {
-  SilcServer server = (SilcServer)context;
-  SilcServerConnection sconn;
+  silc_server_packet_receive,
+  silc_server_packet_eos,
+  silc_server_packet_error
+};
 
-  SILC_LOG_DEBUG(("Connecting to router(s)"));
+/* Parses the packet type and calls what ever routines the packet type
+   requires. This is done for all incoming packets. */
 
-  /* 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"));
+static void silc_server_packet_parse_type(SilcServer server,
+                                         SilcPacketStream sock,
+                                         SilcPacket packet)
+{
+  SilcPacketType type = packet->type;
+  SilcIDListData idata = silc_packet_get_context(sock);
 
-    /* Create connection to the router, if configured. */
-    if (server->config->routers) {
+  SILC_LOG_DEBUG(("Received %s packet [flags %d]",
+                 silc_get_packet_name(type), packet->flags));
 
-      /* 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;
+  /* Parse the packet type */
+  switch (type) {
+  case SILC_PACKET_NOTIFY:
+    /*
+     * Received notify packet. Server can receive notify packets from
+     * router. Server then relays the notify messages to clients if needed.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_notify_list(server, sock, packet);
+    else
+      silc_server_notify(server, sock, packet);
+    break;
 
-      silc_task_register(server->timeout_queue, fd, 
-                        silc_server_connect_router,
-                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                        SILC_TASK_PRI_NORMAL);
-      return;
-    }
-  }
+    /*
+     * Private Message packets
+     */
+  case SILC_PACKET_PRIVATE_MESSAGE:
+    /*
+     * Received private message packet. The packet is coming from either
+     * client or server.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    idata->last_receive = time(NULL);
+    silc_server_private_message(server, sock, packet);
+    break;
 
-  /* If we are a SILC router we need to establish all of our primary
-     routes. */
-  if (server->server_type == SILC_ROUTER) {
-    SilcServerConfigSectionServerConnection *ptr;
+    /*
+     * Channel packets
+     */
+  case SILC_PACKET_CHANNEL_MESSAGE:
+    /*
+     * Received channel message. Channel messages are special packets
+     * (although probably most common ones) thus they are handled
+     * specially.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    idata->last_receive = time(NULL);
+    silc_server_channel_message(server, sock, packet);
+    break;
 
-    SILC_LOG_DEBUG(("We are router"));
+    /*
+     * Command packets
+     */
+  case SILC_PACKET_COMMAND:
+    /*
+     * Recived command. Processes the command request and allocates the
+     * command context and calls the command.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    server->stat.commands_received++;
+    silc_server_command_process(server, sock, packet);
+    break;
 
-    /* Create the connections to all our routes */
-    ptr = server->config->routers;
-    while (ptr) {
+  case SILC_PACKET_COMMAND_REPLY:
+    /*
+     * Received command reply packet. Received command reply to command. It
+     * may be reply to command sent by us or reply to command sent by client
+     * that we've routed further.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    server->stat.commands_received++;
+    silc_server_command_reply(server, sock, packet);
+    break;
 
-      SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
-                     ptr->initiator ? "Initiator" : "Responder",
-                     ptr->host, ptr->port));
+  case SILC_PACKET_DISCONNECT:
+    {
+      SilcStatus status;
+      char *message = NULL;
+      const char *hostname, *ip;
 
-      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 (packet->flags & SILC_PACKET_FLAG_LIST)
+       break;
+      if (silc_buffer_len(&packet->buffer) < 1)
+       break;
 
-      if (!ptr->next)
-       return;
+      status = (SilcStatus)packet->buffer.data[0];
+      if (silc_buffer_len(&packet->buffer) > 1 &&
+         silc_utf8_valid(packet->buffer.data + 1,
+                         silc_buffer_len(&packet->buffer) - 1))
+       message = silc_memdup(packet->buffer.data + 1,
+                             silc_buffer_len(&packet->buffer) - 1);
 
-      ptr = ptr->next;
-    }
-  }
+      if (!silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                                      NULL, &hostname, &ip, NULL))
+       break;
 
-  SILC_LOG_DEBUG(("No router(s), server will be standalone"));
-  
-  /* There wasn't a configured router, we will continue but we don't
-     have a connection to outside world.  We will be standalone server. */
-  server->standalone = TRUE;
-}
+      SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", ip, hostname,
+                    silc_get_status_message(status), status,
+                    message ? message : ""));
 
-/* Second part of connecting to router(s). Key exchange protocol has been
-   executed and now we will execute authentication protocol. */
+      silc_free(message);
 
-SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerKEInternalContext *ctx = 
-    (SilcServerKEInternalContext *)protocol->context;
-  SilcServer server = (SilcServer)ctx->server;
-  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
-  SilcSocketConnection sock = server->sockets[fd];
-  SilcServerConnAuthInternalContext *proto_ctx;
-  SilcServerConfigSectionServerConnection *conn = NULL;
+      /* Do not switch to backup in case of error */
+      server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
 
-  SILC_LOG_DEBUG(("Start"));
+      /* If backup disconnected then mark that resuming will not be allowed */
+      if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         idata->conn_type == SILC_CONN_SERVER) {
+       SilcServerEntry server_entry = (SilcServerEntry)idata;
+       if (server_entry->server_type == SILC_BACKUP_ROUTER)
+         server->backup_closed = TRUE;
+      }
 
-  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);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Key exchange failed");
-    return;
-  }
-  
-  /* We now have the key material as the result of the key exchange
-     protocol. Take the key material into use. Free the raw key material
-     as soon as we've set them into use. */
-  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
-                                       ctx->ske->prop->cipher,
-                                       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);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Key exchange failed");
-    return;
-  }    
-  silc_ske_free_key_material(ctx->keymat);
-
-  /* Allocate internal context for the authentication protocol. This
-     is sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->server = (void *)server;
-  proto_ctx->context = (void *)sconn;
-  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. 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);
+      /* Handle the disconnection from our end too */
+      if (SILC_IS_LOCAL(idata))
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_server_close_connection(server, sock);
+      server->backup_noswitch = FALSE;
     }
-  } else {
-    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);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
-    silc_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    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 */
-  silc_protocol_free(protocol);
-  if (ctx->packet)
-    silc_packet_context_free(ctx->packet);
-  silc_free(ctx);
-  sock->protocol = NULL;
-
-  /* Allocate the authentication protocol. This is allocated here
-     but we won't start it yet. We will be receiving party of this
-     protocol thus we will wait that connecting party will make
-     their first move. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 
-                     &sock->protocol, proto_ctx, 
-                     silc_server_connect_to_router_final);
-
-  /* Register timeout task. If the protocol is not executed inside
-     this timelimit the connection will be terminated. Currently
-     this is 15 seconds and is hard coded limit (XXX). */
-  proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, 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);
-}
+    break;
 
-/* Finalizes the connection to router. Registers a server task to the
-   queue so that we can accept new connections. */
+  case SILC_PACKET_CHANNEL_KEY:
+    /*
+     * Received key for channel. As channels are created by the router
+     * the keys are as well. We will distribute the key to all of our
+     * locally connected clients on the particular channel. Router
+     * never receives this channel and thus is ignored.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_channel_key(server, sock, packet);
+    break;
 
-SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerConnAuthInternalContext *ctx = 
-    (SilcServerConnAuthInternalContext *)protocol->context;
-  SilcServer server = (SilcServer)ctx->server;
-  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
-  SilcSocketConnection sock = ctx->sock;
-  SilcServerEntry id_entry;
-  SilcBuffer packet;
-  SilcServerHBContext hb_context;
-  unsigned char *id_string;
-  uint32 id_len;
-  SilcIDListData idata;
+  case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+    /*
+     * Private message key packet.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_private_message_key(server, sock, packet);
+    break;
 
-  SILC_LOG_DEBUG(("Start"));
+  case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+    /*
+     * Connection authentication request packet. When we receive this packet
+     * we will send to the other end information about our mandatory
+     * authentication method for the connection. This packet maybe received
+     * at any time.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_connection_auth_request(server, sock, packet);
+    break;
 
-  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_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Authentication failed");
-    goto out;
-  }
+  case SILC_PACKET_NEW_ID:
+    /*
+     * Received New ID packet. This includes some new ID that has been
+     * created. It may be for client, server or channel. This is the way
+     * to distribute information about new registered entities in the
+     * SILC network.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_new_id_list(server, sock, packet);
+    else
+      silc_server_new_id(server, sock, packet);
+    break;
 
-  /* 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);
-    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);
-  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(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)),
-                    SILC_STR_END);
-
-  /* Send the packet */
-  silc_server_packet_send(server, ctx->sock, SILC_PACKET_NEW_SERVER, 0,
-                         packet->data, packet->len, TRUE);
-  silc_buffer_free(packet);
-  silc_free(id_string);
-
-  SILC_LOG_DEBUG(("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, strdup(sock->hostname),
-                                   SILC_ROUTER, ctx->dest_id, NULL, sock);
-  if (!id_entry) {
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Authentication failed");
-    goto out;
-  }
+  case SILC_PACKET_NEW_CLIENT:
+    /*
+     * Received new client packet. This includes client information that
+     * we will use to create initial client ID. After creating new
+     * ID we will send it to the client.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_new_client(server, sock, packet);
+    break;
 
-  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
-  silc_free(sock->user_data);
-  sock->user_data = (void *)id_entry;
-  sock->type = SILC_SOCKET_TYPE_ROUTER;
-  server->id_entry->router = id_entry;
-  server->router = id_entry;
-  idata = (SilcIDListData)sock->user_data;
-  idata->registered = TRUE;
-
-  /* Perform keepalive. The `hb_context' will be freed automatically
-     when finally calling the silc_socket_free function. XXX hardcoded 
-     timeout!! */
-  hb_context = silc_calloc(1, sizeof(*hb_context));
-  hb_context->server = server;
-  silc_socket_set_heartbeat(sock, 600, hb_context,
-                           silc_server_perform_heartbeat,
-                           server->timeout_queue);
-
-  /* Register re-key timeout */
-  idata->rekey->timeout = 3600; /* XXX hardcoded */
-  idata->rekey->context = (void *)server;
-  silc_task_register(server->timeout_queue, 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);
+  case SILC_PACKET_NEW_SERVER:
+    /*
+     * Received new server packet. This includes Server ID and some other
+     * information that we may save. This is received after server has
+     * connected to us.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_new_server(server, sock, packet);
+    break;
 
-  /* Announce our clients and channels to the router */
-  silc_server_announce_clients(server);
-  silc_server_announce_channels(server);
+  case SILC_PACKET_NEW_CHANNEL:
+    /*
+     * Received new channel packet. Information about new channel in the
+     * network are distributed using this packet.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_new_channel_list(server, sock, packet);
+    else
+      silc_server_new_channel(server, sock, packet);
+    break;
 
- out:
-  /* Free the temporary connection data context */
-  if (sconn) {
-    silc_free(sconn->remote_host);
-    silc_free(sconn);
-  }
-
-  /* Free the protocol object */
-  silc_protocol_free(protocol);
-  if (ctx->packet)
-    silc_packet_context_free(ctx->packet);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  silc_free(ctx);
-  sock->protocol = NULL;
-}
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    break;
 
-/* Accepts new connections to the server. Accepting new connections are
-   done in three parts to make it async. */
+  case SILC_PACKET_KEY_AGREEMENT:
+    /*
+     * Received heartbeat.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_key_agreement(server, sock, packet);
+    break;
 
-SILC_TASK_CALLBACK(silc_server_accept_new_connection)
-{
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
-  SilcServerKEInternalContext *proto_ctx;
-  int sock, port;
-  void *cconfig, *sconfig, *rconfig;
-  SilcServerConfigSectionDenyConnection *deny;
+  case SILC_PACKET_REKEY:
+    /*
+     * Received re-key packet. The sender wants to regenerate the session
+     * keys.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_rekey(server, sock, packet);
+    break;
 
-  SILC_LOG_DEBUG(("Accepting new connection"));
+  case SILC_PACKET_FTP:
+    /* FTP packet */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_ftp(server, sock, packet);
+    break;
 
-  server->stat.conn_attempts++;
+  case SILC_PACKET_RESUME_CLIENT:
+    /* Resume client */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_resume_client(server, sock, packet);
+    break;
 
-  sock = silc_net_accept_connection(server->sock);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
-    server->stat.conn_failures++;
-    return;
-  }
+  case SILC_PACKET_RESUME_ROUTER:
+    /* Resume router packet received. This packet is received for backup
+       router resuming protocol. */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_backup_resume_router(server, sock, packet);
+    break;
 
-  /* Check max connections */
-  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    SILC_LOG_ERROR(("Refusing connection, server is full"));
-    server->stat.conn_failures++;
-    return;
+  default:
+    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+    break;
   }
+}
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* We don't create a ID yet, since we don't know what type of connection
-     this is yet. But, we do add the connection to the socket table. */
-  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. */
-  if (!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 %s",
-                     newsocket->hostname ? newsocket->hostname :
-                     newsocket->ip ? newsocket->ip : ""));
-      server->stat.conn_failures++;
-      return;
-    }
-    if (!newsocket->hostname)
-      newsocket->hostname = strdup(newsocket->ip);
-  }
-  newsocket->port = silc_net_get_remote_port(sock);
+/****************************** Server API **********************************/
 
-  /* 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);
+/* 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
+   the server. The new allocated server object is returned to the new_server
+   argument. */
 
-  port = server->sockets[fd]->port; /* Listenning port */
+SilcBool silc_server_alloc(SilcServer *new_server)
+{
+  SilcServer server;
 
-  /* Check whether this connection is denied to connect to us. */
-  deny = silc_server_config_denied_conn(server->config, newsocket->ip, port);
-  if (!deny)
-    deny = silc_server_config_denied_conn(server->config, newsocket->hostname,
-                                         port);
-  if (deny) {
-    /* The connection is denied */
-    silc_server_disconnect_remote(server, newsocket, deny->comment ?
-                                 deny->comment :
-                                 "Server closed connection: "
-                                 "Connection refused");
-    server->stat.conn_failures++;
-    return;
-  }
+  SILC_LOG_DEBUG(("Allocating new server object"));
 
-  /* 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,
-                                                     newsocket->ip, port)))
-    cconfig = silc_server_config_find_client_conn(server->config,
-                                                 newsocket->hostname, 
-                                                 port);
-  if (!(sconfig = silc_server_config_find_server_conn(server->config,
-                                                    newsocket->ip, 
-                                                    port)))
-    sconfig = silc_server_config_find_server_conn(server->config,
-                                                 newsocket->hostname,
-                                                 port);
-  if (!(rconfig = silc_server_config_find_router_conn(server->config,
-                                                    newsocket->ip, port)))
-    rconfig = silc_server_config_find_router_conn(server->config,
-                                                 newsocket->hostname, 
-                                                 port);
-  if (!cconfig && !sconfig && !rconfig) {
-    silc_server_disconnect_remote(server, newsocket, 
-                                 "Server closed connection: "
-                                 "Connection refused");
-    server->stat.conn_failures++;
-    return;
-  }
+  server = silc_calloc(1, sizeof(*server));
+  if (!server)
+    return FALSE;
+  server->server_type = SILC_SERVER;
+  server->standalone = TRUE;
+  server->local_list = silc_calloc(1, sizeof(*server->local_list));
+  if (!server->local_list)
+    return FALSE;
+  server->global_list = silc_calloc(1, sizeof(*server->global_list));
+  if (!server->global_list)
+    return FALSE;
+  server->pending_commands = silc_dlist_init();
+  if (!server->pending_commands)
+    return FALSE;
+  server->listeners = silc_dlist_init();
+  if (!server->listeners)
+    return FALSE;
+  server->repository = silc_skr_alloc();
+  if (!server->repository)
+    return FALSE;
+  server->conns = silc_dlist_init();
+  if (!server->conns)
+    return FALSE;
+  server->expired_clients = silc_dlist_init();
+  if (!server->expired_clients)
+    return FALSE;
 
-  /* The connection is allowed */
+  *new_server = server;
 
-  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;
-  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, 
-                     &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);
+  return TRUE;
 }
 
-/* Second part of accepting new connection. Key exchange protocol has been
-   performed and now it is time to do little connection authentication
-   protocol to figure out whether this connection is client or server
-   and whether it has right to access this server (especially server
-   connections needs to be authenticated). */
+/* Free's the SILC server object. This is called at the very end before
+   the program ends. */
 
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
+void silc_server_free(SilcServer server)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerKEInternalContext *ctx = 
-    (SilcServerKEInternalContext *)protocol->context;
-  SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = server->sockets[fd];
-  SilcServerConnAuthInternalContext *proto_ctx;
+  SilcList list;
+  SilcIDCacheEntry cache;
+  SilcIDListData idata;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Free server %p", server));
 
-  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);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Key exchange failed");
-    server->stat.auth_failures++;
+  if (!server)
     return;
+
+  silc_server_backup_free(server);
+  silc_server_config_unref(&server->config_ref);
+  if (server->rng)
+    silc_rng_free(server->rng);
+  if (server->public_key)
+    silc_pkcs_public_key_free(server->public_key);
+  if (server->private_key)
+    silc_pkcs_private_key_free(server->private_key);
+  if (server->pending_commands)
+    silc_dlist_uninit(server->pending_commands);
+  if (server->id_entry) {
+    if (server->id_entry->data.sconn)
+      silc_schedule_task_del_by_context(server->schedule,
+                                       server->id_entry->data.sconn->sock);
+    silc_idlist_del_server(server->local_list, server->id_entry);
   }
 
-  /* We now have the key material as the result of the key exchange
-     protocol. Take the key material into use. Free the raw key material
-     as soon as we've set them into use. */
-  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
-                                       ctx->ske->prop->cipher,
-                                       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);
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Key exchange failed");
-    server->stat.auth_failures++;
-    return;
-  }    
-  silc_ske_free_key_material(ctx->keymat);
-
-  /* Allocate internal context for the authentication protocol. This
-     is sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->server = (void *)server;
-  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);
-  if (ctx->packet)
-    silc_packet_context_free(ctx->packet);
-  silc_free(ctx);
-  sock->protocol = NULL;
-
-  /* Allocate the authentication protocol. This is allocated here
-     but we won't start it yet. We will be receiving party of this
-     protocol thus we will wait that connecting party will make
-     their first move. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 
-                     &sock->protocol, proto_ctx, 
-                     silc_server_accept_new_connection_final);
-
-  /* Register timeout task. If the protocol is not executed inside
-     this timelimit the connection will be terminated. Currently
-     this is 60 seconds and is hard coded limit (XXX). */
-  proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock->sock, 
-                      silc_server_timeout_remote,
-                      (void *)server, 60, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+  /* Delete all channels */
+  if (silc_idcache_get_all(server->local_list->channels, &list)) {
+    silc_list_start(list);
+    while ((cache = silc_list_get(list)))
+      silc_idlist_del_channel(server->local_list, cache->context);
+  }
+  if (silc_idcache_get_all(server->global_list->channels, &list)) {
+    silc_list_start(list);
+    while ((cache = silc_list_get(list)))
+      silc_idlist_del_channel(server->global_list, cache->context);
+  }
+
+  /* Delete all clients */
+  if (silc_idcache_get_all(server->local_list->clients, &list)) {
+    silc_list_start(list);
+    while ((cache = silc_list_get(list))) {
+      silc_schedule_task_del_by_context(server->schedule, cache->context);
+      silc_idlist_del_client(server->local_list, cache->context);
+    }
+  }
+  if (silc_idcache_get_all(server->global_list->clients, &list)) {
+    silc_list_start(list);
+    while ((cache = silc_list_get(list))) {
+      silc_schedule_task_del_by_context(server->schedule, cache->context);
+      silc_idlist_del_client(server->global_list, cache->context);
+    }
+  }
+
+  /* Delete all servers */
+  if (silc_idcache_get_all(server->local_list->servers, &list)) {
+    silc_list_start(list);
+    while ((cache = silc_list_get(list))) {
+      idata = (SilcIDListData)cache->context;
+      if (idata->sconn)
+       silc_schedule_task_del_by_context(server->schedule,
+                                         idata->sconn->sock);
+      silc_idlist_del_server(server->local_list, cache->context);
+    }
+  }
+  if (silc_idcache_get_all(server->global_list->servers, &list)) {
+    while ((cache = silc_list_get(list))) {
+      idata = (SilcIDListData)cache->context;
+      if (idata->sconn)
+       silc_schedule_task_del_by_context(server->schedule,
+                                         idata->sconn->sock);
+      silc_idlist_del_server(server->global_list, cache->context);
+    }
+  }
+
+  silc_schedule_task_del_by_context(server->schedule, server);
+  silc_schedule_uninit(server->schedule);
+  server->schedule = NULL;
+
+  silc_idcache_free(server->local_list->clients);
+  silc_idcache_free(server->local_list->servers);
+  silc_idcache_free(server->local_list->channels);
+  silc_idcache_free(server->global_list->clients);
+  silc_idcache_free(server->global_list->servers);
+  silc_idcache_free(server->global_list->channels);
+  silc_hash_table_free(server->watcher_list);
+  silc_hash_table_free(server->watcher_list_pk);
+  silc_hash_free(server->md5hash);
+  silc_hash_free(server->sha1hash);
+
+  silc_dlist_uninit(server->listeners);
+  silc_dlist_uninit(server->conns);
+  silc_dlist_uninit(server->expired_clients);
+  silc_skr_free(server->repository);
+  silc_packet_engine_stop(server->packet_engine);
+
+  silc_free(server->local_list);
+  silc_free(server->global_list);
+  silc_free(server->server_name);
+  silc_free(server);
+
+  silc_hmac_unregister_all();
+  silc_hash_unregister_all();
+  silc_cipher_unregister_all();
+  silc_pkcs_unregister_all();
 }
 
-/* Final part of accepting new connection. The connection has now
-   been authenticated and keys has been exchanged. We also know whether
-   this is client or server connection. */
+/* Creates a new server listener. */
 
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
+static SilcNetListener
+silc_server_listen(SilcServer server, const char *server_ip, SilcUInt16 port)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerConnAuthInternalContext *ctx = 
-    (SilcServerConnAuthInternalContext *)protocol->context;
-  SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = ctx->sock;
-  SilcServerHBContext hb_context;
-  void *id_entry = NULL;
+  SilcNetListener listener;
+
+  listener =
+    silc_net_tcp_create_listener(&server_ip, 1, port, TRUE,
+                                server->config->require_reverse_lookup,
+                                server->schedule,
+                                silc_server_accept_new_connection, server);
+  if (!listener) {
+    SILC_SERVER_LOG_ERROR(("Could not create server listener: %s on %hu",
+                          server_ip, port));
+    return NULL;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  return listener;
+}
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      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);
-    if (sock)
-      sock->protocol = NULL;
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_failure_callback);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Authentication failed");
-    server->stat.auth_failures++;
-    return;
+/* Adds a secondary listener. */
+
+SilcBool silc_server_init_secondary(SilcServer server)
+{
+  SilcServerConfigServerInfoInterface *interface;
+  SilcNetListener listener;
+
+  for (interface = server->config->server_info->secondary; interface;
+       interface = interface->next) {
+    listener = silc_server_listen(server, interface->server_ip,
+                                 interface->port);
+    if (!listener)
+      return FALSE;
+    silc_dlist_add(server->listeners, listener);
   }
 
-  sock->type = ctx->conn_type;
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    {
-      SilcClientEntry client;
+  return TRUE;
+}
 
-      SILC_LOG_DEBUG(("Remote host is client"));
-      SILC_LOG_INFO(("Connection from %s (%s) is client", sock->hostname,
-                    sock->ip));
+/* Initializes the entire SILC server. This is called always before running
+   the server. This is called only once at the initialization of the program.
+   This binds the server to its listenning port. After this function returns
+   one should call silc_server_run to start the server. This returns TRUE
+   when everything is ok to run the server. Configuration file must be
+   read and parsed before calling this. */
 
-      /* Add the client to the client ID cache. The nickname and Client ID
-        and other information is created after we have received NEW_CLIENT
-        packet from client. */
-      client = silc_idlist_add_client(server->local_list, 
-                                     NULL, 0, NULL, NULL, NULL, NULL, sock);
-      if (!client) {
-       SILC_LOG_ERROR(("Could not add new client to cache"));
-       silc_free(sock->user_data);
-       break;
-      }
+SilcBool silc_server_init(SilcServer server)
+{
+  SilcServerID *id;
+  SilcServerEntry id_entry;
+  SilcNetListener listener;
+  SilcUInt16 *port;
+  char **ip;
 
-      /* Statistics */
-      server->stat.my_clients++;
-      server->stat.clients++;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.cell_clients++;
+  SILC_LOG_DEBUG(("Initializing server"));
 
-      id_entry = (void *)client;
-      break;
-    }
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    {
-      SilcServerEntry new_server;
-      SilcServerConfigSectionServerConnection *conn = 
-       sock->type == SILC_SOCKET_TYPE_SERVER ? ctx->sconfig : ctx->rconfig;
+  server->starttime = time(NULL);
 
-      SILC_LOG_DEBUG(("Remote host is %s", 
-                     sock->type == SILC_SOCKET_TYPE_SERVER ? 
-                     "server" : "router"));
-      SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
-                    sock->ip, sock->type == SILC_SOCKET_TYPE_SERVER ? 
-                    "server" : "router"));
+  /* Take config object for us */
+  silc_server_config_ref(&server->config_ref, server->config,
+                        server->config);
 
-      /* Add the server into server cache. The server name and Server ID
-        is updated after we have received NEW_SERVER packet from the
-        server. We mark ourselves as router for this server if we really
-        are router. */
-      new_server = 
-       silc_idlist_add_server(server->local_list, NULL,
-                              sock->type == SILC_SOCKET_TYPE_SERVER ?
-                              SILC_SERVER : SILC_ROUTER, NULL, 
-                              sock->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;
-      }
+#ifdef SILC_DEBUG
+  /* Set debugging on if configured */
+  if (server->config->debug_string) {
+    silc_log_debug(TRUE);
+    silc_log_set_debug_string(server->config->debug_string);
+  }
+#endif /* SILC_DEBUG */
 
-      /* Statistics */
-      if (sock->type == SILC_SOCKET_TYPE_SERVER)
-       server->stat.my_servers++;
-      else
-       server->stat.my_routers++;
-      server->stat.servers++;
+  /* Steal public and private key from the config object */
+  server->public_key = server->config->server_info->public_key;
+  server->private_key = server->config->server_info->private_key;
+  server->config->server_info->public_key = NULL;
+  server->config->server_info->private_key = NULL;
 
-      id_entry = (void *)new_server;
+  /* Register all configured ciphers, PKCS and hash functions. */
+  if (!silc_server_config_register_ciphers(server))
+    silc_cipher_register_default();
+  if (!silc_server_config_register_pkcs(server))
+    silc_pkcs_register_default();
+  if (!silc_server_config_register_hashfuncs(server))
+    silc_hash_register_default();
+  if (!silc_server_config_register_hmacs(server))
+    silc_hmac_register_default();
 
-      /* Check whether this connection is to be our primary router connection
-        if we dont' already have the primary route. */
-      if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
-       if (silc_server_config_is_primary_route(server->config) &&
-           !conn->initiator)
-         break;
+  /* Initialize random number generator for the server. */
+  server->rng = silc_rng_alloc();
+  silc_rng_init(server->rng);
+  silc_rng_global_init(server->rng);
 
-       SILC_LOG_DEBUG(("We are not standalone server anymore"));
-       server->standalone = FALSE;
-       if (!server->id_entry->router) {
-         server->id_entry->router = id_entry;
-         server->router = id_entry;
-       }
-      }
-
-      break;
-    }
-  default:
-    break;
-  }
+  /* Initialize hash functions for server to use */
+  silc_hash_alloc("md5", &server->md5hash);
+  silc_hash_alloc("sha1", &server->sha1hash);
 
-  /* Add the common data structure to the ID entry. */
-  if (id_entry)
-    silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
-      
-  /* Add to sockets internal pointer for fast referencing */
-  silc_free(sock->user_data);
-  sock->user_data = id_entry;
+  /* Initialize the scheduler */
+  server->schedule = silc_schedule_init(server->config->param.connections_max,
+                                       server);
+  if (!server->schedule)
+    goto err;
 
-  /* Connection has been fully established now. Everything is ok. */
-  SILC_LOG_DEBUG(("New connection authenticated"));
-
-  /* Perform keepalive. The `hb_context' will be freed automatically
-     when finally calling the silc_socket_free function. XXX hardcoded 
-     timeout!! */
-  hb_context = silc_calloc(1, sizeof(*hb_context));
-  hb_context->server = server;
-  silc_socket_set_heartbeat(sock, 600, hb_context,
-                           silc_server_perform_heartbeat,
-                           server->timeout_queue);
-
-  silc_task_unregister_by_callback(server->timeout_queue,
-                                  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);
-  sock->protocol = NULL;
-}
+  /* First, register log files configuration for error output */
+  silc_server_config_setlogfiles(server);
 
-/* This function is used to read packets from network and send packets to
-   network. This is usually a generic task. */
+  /* Initialize ID caches */
+  server->local_list->clients =
+    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+                      server);
+  server->local_list->servers =
+    silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+                      server);
+  server->local_list->channels =
+    silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+                      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_ID_CLIENT, silc_idlist_client_destructor,
+                      server);
+  server->global_list->servers =
+    silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+                      server);
+  server->global_list->channels =
+    silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+                      NULL);
+
+  /* Init watcher lists */
+  server->watcher_list =
+    silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
+                         silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
+                         NULL, NULL, TRUE);
+  if (!server->watcher_list)
+    goto err;
+  server->watcher_list_pk =
+    silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+                         silc_hash_public_key_compare, NULL,
+                         NULL, NULL, TRUE);
+  if (!server->watcher_list_pk)
+    goto err;
+
+  /* Create TCP listener */
+  listener = silc_server_listen(
+                  server,
+                  server->config->server_info->primary == NULL ? NULL :
+                  server->config->server_info->primary->server_ip,
+                  server->config->server_info->primary == NULL ? 0 :
+                  server->config->server_info->primary->port);
+  if (!listener)
+    goto err;
+  silc_dlist_add(server->listeners, listener);
+
+  /* Create a Server ID for the server. */
+  port = silc_net_listener_get_port(listener, NULL);
+  ip = silc_net_listener_get_ip(listener, NULL);
+  silc_id_create_server_id(server->config->server_info->primary->public_ip ?
+                          server->config->server_info->primary->public_ip :
+                          ip[0], port[0], server->rng, &id);
+  if (!id)
+    goto err;
+
+  silc_free(port);
+  silc_free(ip[0]);
+  silc_free(ip);
+
+  server->id = id;
+  server->server_name = server->config->server_info->server_name;
+  server->config->server_info->server_name = NULL;
+  silc_id_id2str(server->id, SILC_ID_SERVER, server->id_string,
+                sizeof(server->id_string), &server->id_string_len);
+
+  /* Add ourselves to the server list. We don't have a router yet
+     beacuse we haven't established a route yet. It will be done later.
+     For now, NULL is sent as router. This allocates new entry to
+     the ID list. */
+  id_entry =
+    silc_idlist_add_server(server->local_list, strdup(server->server_name),
+                          server->server_type,
+                          silc_id_dup(server->id, SILC_ID_SERVER),
+                          NULL, NULL);
+  if (!id_entry) {
+    SILC_LOG_ERROR(("Could not add local server to cache"));
+    goto err;
+  }
+  id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+  id_entry->data.conn_type = (server->server_type == SILC_SERVER ?
+                             SILC_CONN_SERVER : SILC_CONN_ROUTER);
+  server->id_entry = id_entry;
 
-SILC_TASK_CALLBACK(silc_server_packet_process)
-{
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
-  SilcIDListData idata;
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
-  int ret;
+  /* Create secondary TCP listeners */
+  if (silc_server_init_secondary(server) == FALSE)
+    goto err;
 
-  if (!sock)
-    return;
+  server->listenning = TRUE;
 
-  SILC_LOG_DEBUG(("Processing packet"));
+  /* Create connections to configured routers. */
+  silc_server_create_connections(server);
 
-  /* Packet sending */
+  /* 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) {
+    SilcServerConfigServer *ptr = server->config->servers;
 
-  if (type == SILC_TASK_WRITE) {
-    /* Do not send data to disconnected connection */
-    if (SILC_IS_DISCONNECTED(sock))
-      return;
+    server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       server->id_entry->server_type = SILC_BACKUP_ROUTER;
+       break;
+      }
+      ptr = ptr->next;
+    }
+  }
 
-    server->stat.packets_sent++;
+  if (server->server_type != SILC_ROUTER) {
+    server->stat.servers = 1;
+    server->stat.cell_servers = 1;
+  } else {
+    server->stat.routers = 1;
+  }
 
-    if (sock->outbuf->data - sock->outbuf->head)
-     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+  /* If we are normal server we'll retrieve network statisticial information
+     once in a while from the router. */
+  if (server->server_type != SILC_ROUTER)
+    silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
+                                  server, 10, 0);
 
-    /* Send the packet */
-    ret = silc_packet_send(sock, TRUE);
+  /* Start packet engine */
+  server->packet_engine =
+    silc_packet_engine_start(server->rng, server->server_type == SILC_ROUTER,
+                            &silc_server_stream_cbs, server);
+  if (!server->packet_engine)
+    goto err;
 
-    /* If returned -2 could not write to connection now, will do
-       it later. */
-    if (ret == -2)
-      return;
+  /* Register client entry expiration timeout */
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_purge_expired_clients, server,
+                                120, 0);
 
-    if (ret == -1)
-      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_UNSET_OUTBUF_PENDING(sock);
-
-    silc_buffer_clear(sock->outbuf);
-    return;
-  }
+  /* Initialize HTTP server */
+  silc_server_http_init(server);
 
-  /* Packet receiving */
+  SILC_LOG_DEBUG(("Server initialized"));
 
-  /* Read some data from connection */
-  ret = silc_packet_receive(sock);
-  if (ret < 0)
-    return;
-    
-  /* EOF */
-  if (ret == 0) {
-    SILC_LOG_DEBUG(("Read EOF"));
-      
-    /* If connection is disconnecting already we will finally
-       close the connection */
-    if (SILC_IS_DISCONNECTING(sock)) {
-      if (sock->user_data)
-       silc_server_free_sock_user_data(server, sock);
-      silc_server_close_connection(server, sock);
-      return;
-    }
-      
-    SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
-    SILC_SET_DISCONNECTING(sock);
-
-    /* If the closed connection was our primary router connection the
-       start re-connecting phase. */
-    if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && 
-       sock == server->router->connection)
-      silc_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);
-    return;
-  }
+  /* We are done here, return succesfully */
+  return TRUE;
 
-  /* If connection is disconnecting or disconnected we will ignore
-     what we read. */
-  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
-    SILC_LOG_DEBUG(("Ignoring read data from disonnected connection"));
-    return;
-  }
+ err:
+  silc_server_config_unref(&server->config_ref);
+  return FALSE;
+}
 
-  server->stat.packets_received++;
+/* Task callback to close a socket connection after rehash */
 
-  /* Get keys and stuff from ID entry */
-  idata = (SilcIDListData)sock->user_data;
-  if (idata) {
-    idata->last_receive = time(NULL);
-    cipher = idata->receive_key;
-    hmac = idata->hmac_receive;
-  }
-  /* Process the packet. This will call the parser that will then
-     decrypt and parse the packet. */
-  silc_packet_receive_process(sock, cipher, hmac, silc_server_packet_parse, 
-                             server);
+SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
+{
+  SilcServer server = app_context;
+  SilcPacketStream sock = context;
+  SilcIDListData idata = silc_packet_get_context(sock);
+  const char *hostname;
+  SilcUInt16 port;
+
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                             NULL, &hostname, NULL, &port);
+
+  SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
+                hostname, port, SILC_CONNTYPE_STRING(idata->conn_type)));
+  silc_schedule_task_del_by_context(server->schedule, sock);
+  silc_server_disconnect_remote(server, sock,
+                               SILC_STATUS_ERR_BANNED_FROM_SERVER,
+                               "This connection is removed from "
+                               "configuration");
+  silc_server_free_sock_user_data(server, sock, NULL);
 }
 
-/* Callback function that the silc_packet_decrypt will call to make the
-   decision whether the packet is normal or special packet. We will 
-   return TRUE if it is normal and FALSE if it is special */
+/* This function basically reads the config file again and switches the config
+   object pointed by the server object. After that, we have to fix various
+   things such as the server_name and the listening ports.
+   Keep in mind that we no longer have the root privileges at this point. */
 
-static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
-                                           SilcBuffer buffer,
-                                           SilcPacketContext *packet,
-                                           void *context)
+SilcBool silc_server_rehash(SilcServer server)
 {
-  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
-  SilcServer server = (SilcServer)parse_ctx->context;
-
-  /* Packet is normal packet, if: 
+  SilcServerConfig newconfig;
 
-     1) packet is private message packet and does not have private key set
-     2) is other packet than channel message packet
-     3) is channel message packet and remote is router and we are router 
+  SILC_LOG_INFO(("Rehashing server"));
 
-     all other packets are special packets 
-  */
+  /* Reset the logging system */
+  silc_log_quick(TRUE);
+  silc_log_flush_all();
 
-  if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
-      (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+  /* Start the main rehash phase (read again the config file) */
+  newconfig = silc_server_config_alloc(server->config_file, server);
+  if (!newconfig) {
+    SILC_LOG_ERROR(("Rehash FAILED."));
     return FALSE;
+  }
 
-  if (packet_type != SILC_PACKET_CHANNEL_MESSAGE || 
-      (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
-       parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
-       server->server_type == SILC_ROUTER))
-    return TRUE;
+  /* Fix the server_name field */
+  if (strcmp(server->server_name, newconfig->server_info->server_name)) {
+    silc_free(server->server_name);
+
+    /* Check server name */
+    server->server_name =
+      silc_identifier_check(newconfig->server_info->server_name,
+                           strlen(newconfig->server_info->server_name),
+                           SILC_STRING_LOCALE, 256, NULL);
+    if (!server->server_name) {
+      SILC_LOG_ERROR(("Malformed server name string '%s'",
+                     server->config->server_info->server_name));
+      return FALSE;
+    }
 
-  return FALSE;
-}
-  
-/* Parses whole packet, received earlier. */
+    /* Update the idcache list with a fresh pointer */
+    silc_free(server->id_entry->server_name);
+    server->id_entry->server_name = strdup(server->server_name);
+    silc_idcache_update_by_context(server->local_list->servers,
+                                  server->id_entry, NULL,
+                                  strdup(server->id_entry->server_name),
+                                  TRUE);
+  }
 
-SILC_TASK_CALLBACK(silc_server_packet_parse_real)
-{
-  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
-  SilcServer server = (SilcServer)parse_ctx->context;
-  SilcSocketConnection sock = parse_ctx->sock;
-  SilcPacketContext *packet = parse_ctx->packet;
-  SilcIDListData idata = (SilcIDListData)sock->user_data;
-  int ret;
+  /* Set logging */
+  silc_server_config_setlogfiles(server);
+
+  /* Change new key pair if necessary */
+  if (newconfig->server_info->public_key &&
+      !silc_pkcs_public_key_compare(server->public_key,
+                                   newconfig->server_info->public_key)) {
+    silc_pkcs_public_key_free(server->public_key);
+    silc_pkcs_private_key_free(server->private_key);
+    server->public_key = newconfig->server_info->public_key;
+    server->private_key = newconfig->server_info->private_key;
+    newconfig->server_info->public_key = NULL;
+    newconfig->server_info->private_key = NULL;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Check for unconfigured server and router connections and close
+     connections that were unconfigured. */
 
-  /* Decrypt the received packet */
-  ret = silc_packet_decrypt(idata ? idata->receive_key : NULL, 
-                           idata ? idata->hmac_receive : NULL, 
-                           packet->buffer, packet,
-                           silc_server_packet_decrypt_check, parse_ctx);
-  if (ret < 0)
-    goto out;
+  if (server->config->routers) {
+    SilcServerConfigRouter *ptr;
+    SilcServerConfigRouter *newptr;
+    SilcBool found;
 
-  if (ret == 0) {
-    /* Parse the packet. Packet type is returned. */
-    ret = silc_packet_parse(packet);
-  } else {
-    /* Parse the packet header in special way as this is "special"
-       packet type. */
-    ret = silc_packet_parse_special(packet);
+    for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+      found = FALSE;
+
+      /* Check whether new config has this one too */
+      for (newptr = newconfig->routers; newptr; newptr = newptr->next) {
+       if (silc_string_compare(newptr->host, ptr->host) &&
+           newptr->port == ptr->port &&
+           newptr->initiator == ptr->initiator) {
+         found = TRUE;
+         break;
+       }
+      }
+
+      if (!found && ptr->host) {
+       /* Remove this connection */
+       SilcPacketStream sock;
+       sock = silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+                                              ptr->host, ptr->port);
+       if (sock)
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_rehash_close_connection,
+                                        sock, 0, 1);
+      }
+    }
   }
 
-  if (ret == SILC_PACKET_NONE)
-    goto out;
+  if (server->config->servers) {
+    SilcServerConfigServer *ptr;
+    SilcServerConfigServer *newptr;
+    SilcBool found;
 
-  /* Check that the the current client ID is same as in the client's packet. */
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    SilcClientEntry client = (SilcClientEntry)sock->user_data;
-    if (client && client->id) {
-      void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                               packet->src_id_type);
-      if (!id || !SILC_ID_CLIENT_COMPARE(client->id, id)) {
-       silc_free(id);
-       goto out;
+    for (ptr = server->config->servers; ptr; ptr = ptr->next) {
+      found = FALSE;
+
+      /* Check whether new config has this one too */
+      for (newptr = newconfig->servers; newptr; newptr = newptr->next) {
+       if (silc_string_compare(newptr->host, ptr->host)) {
+         found = TRUE;
+         break;
+       }
+      }
+
+      if (!found && ptr->host) {
+       /* Remove this connection */
+       SilcPacketStream sock;
+       sock = silc_server_find_socket_by_host(server, SILC_CONN_SERVER,
+                                              ptr->host, 0);
+       if (sock)
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_rehash_close_connection,
+                                        sock, 0, 1);
       }
-      silc_free(id);
     }
   }
 
-  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->flags & SILC_PACKET_FLAG_BROADCAST) &&
-       packet->dst_id_type == SILC_ID_SERVER && 
-       sock->type != SILC_SOCKET_TYPE_CLIENT &&
-       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, 
-                               packet->dst_id_type);
-      if (!id)
-       goto out;
-      silc_server_packet_route(server,
-                              silc_server_route_get(server, id,
-                                                    packet->dst_id_type),
-                              packet);
-      silc_free(id);
-      goto out;
+  if (server->config->clients) {
+    SilcServerConfigClient *ptr;
+    SilcServerConfigClient *newptr;
+    SilcBool found;
+
+    for (ptr = server->config->clients; ptr; ptr = ptr->next) {
+      found = FALSE;
+
+      /* Check whether new config has this one too */
+      for (newptr = newconfig->clients; newptr; newptr = newptr->next) {
+       if (silc_string_compare(newptr->host, ptr->host)) {
+         found = TRUE;
+         break;
+       }
+      }
+
+      if (!found && ptr->host) {
+       /* Remove this connection */
+       SilcPacketStream sock;
+       sock = silc_server_find_socket_by_host(server, SILC_CONN_CLIENT,
+                                              ptr->host, 0);
+       if (sock)
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_rehash_close_connection,
+                                        sock, 0, 1);
+      }
     }
   }
 
-  /* Parse the incoming packet type */
-  silc_server_packet_parse_type(server, sock, packet);
+  /* Create connections after rehash */
+  silc_server_create_connections(server);
 
-  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 &&
-       !server->standalone) {
-      silc_server_packet_broadcast(server, server->router->connection, packet);
+  /* Check whether our router status has changed */
+  if (newconfig->servers) {
+    SilcServerConfigServer *ptr = newconfig->servers;
+
+    server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       server->id_entry->server_type = SILC_BACKUP_ROUTER;
+       break;
+      }
+      ptr = ptr->next;
     }
   }
 
- out:
-  /*  silc_buffer_clear(sock->inbuf); */
-  silc_packet_context_free(packet);
-  silc_free(parse_ctx);
+  /* Our old config is gone now. We'll unreference our reference made in
+     silc_server_init and then destroy it since we are destroying it
+     underneath the application (layer which called silc_server_init). */
+  silc_server_config_unref(&server->config_ref);
+  silc_server_config_destroy(server->config);
+
+  /* Take new config context */
+  server->config = newconfig;
+  silc_server_config_ref(&server->config_ref, server->config, server->config);
+
+#ifdef SILC_DEBUG
+  /* Set debugging on if configured */
+  if (server->config->debug_string) {
+    silc_log_debug(TRUE);
+    silc_log_set_debug_string(server->config->debug_string);
+  }
+#endif /* SILC_DEBUG */
+
+  SILC_LOG_DEBUG(("Server rehashed"));
+
+  return TRUE;
 }
 
-/* Parser callback called by silc_packet_receive_process. This merely
-   registers timeout that will handle the actual parsing when appropriate. */
+/* The heart of the server. This runs the scheduler thus runs the server.
+   When this returns the server has been stopped and the program will
+   be terminated. */
 
-void silc_server_packet_parse(SilcPacketParserContext *parser_context)
+void silc_server_run(SilcServer server)
 {
-  SilcServer server = (SilcServer)parser_context->context;
-  SilcSocketConnection sock = parser_context->sock;
+  SILC_LOG_INFO(("SILC Server started"));
 
-  switch (sock->type) {
-  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);
-    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);
-    break;
-  default:
-    return;
-  }
+  /* Start the scheduler, the heart of the SILC server. When this returns
+     the program will be terminated. */
+  silc_schedule(server->schedule);
 }
 
-/* Parses the packet type and calls what ever routines the packet type
-   requires. This is done for all incoming packets. */
+/* 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_packet_parse_type(SilcServer server, 
-                                  SilcSocketConnection sock,
-                                  SilcPacketContext *packet)
+void silc_server_stop(SilcServer server)
 {
-  SilcPacketType type = packet->type;
+  SilcDList list;
+  SilcPacketStream ps;
+  SilcNetListener listener;
 
-  SILC_LOG_DEBUG(("Parsing packet type %d", type));
+  SILC_LOG_INFO(("SILC Server shutting down"));
 
-  /* Parse the packet type */
-  switch(type) {
-  case SILC_PACKET_DISCONNECT:
-    SILC_LOG_DEBUG(("Disconnect packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    break;
+  server->server_shutdown = TRUE;
 
-  case SILC_PACKET_SUCCESS:
-    /*
-     * Success received for something. For now we can have only
-     * one protocol for connection executing at once hence this
-     * success message is for whatever protocol is executing currently.
-     */
-    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);
-    }
-    break;
+  /* Close all connections */
+  if (server->packet_engine) {
+    list = silc_packet_engine_get_streams(server->packet_engine);
 
-  case SILC_PACKET_FAILURE:
-    /*
-     * Failure received for something. For now we can have only
-     * one protocol for connection executing at once hence this
-     * failure message is for whatever protocol is executing currently.
-     */
-    SILC_LOG_DEBUG(("Failure packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    if (sock->protocol) {
-      SilcServerFailureContext f;
-      f = silc_calloc(1, sizeof(*f));
-      f->server = server;
-      f->sock = sock;
-      
-      /* We will wait 5 seconds to process this failure packet */
-      silc_task_register(server->timeout_queue, sock->sock,
-                        silc_server_failure_callback, (void *)f, 5, 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-    }
-    break;
+    silc_dlist_start(list);
+    while ((ps = silc_dlist_get(list))) {
+      SilcIDListData idata = silc_packet_get_context(ps);
 
-  case SILC_PACKET_REJECT:
-    SILC_LOG_DEBUG(("Reject packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    return;
-    break;
+      if (!silc_packet_stream_is_valid(ps))
+       continue;
 
-  case SILC_PACKET_NOTIFY:
-    /*
-     * Received notify packet. Server can receive notify packets from
-     * router. Server then relays the notify messages to clients if needed.
-     */
-    SILC_LOG_DEBUG(("Notify packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      silc_server_notify_list(server, sock, packet);
-    else
-      silc_server_notify(server, sock, packet);
-    break;
+      if (idata)
+       idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
 
-    /* 
-     * Channel packets
-     */
-  case SILC_PACKET_CHANNEL_MESSAGE:
-    /*
-     * Received channel message. Channel messages are special packets
-     * (although probably most common ones) thus they are handled
-     * specially.
-     */
-    SILC_LOG_DEBUG(("Channel Message packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_channel_message(server, sock, packet);
-    break;
+      silc_server_disconnect_remote(server, ps, SILC_STATUS_OK,
+                                   "Server is shutting down");
+      silc_server_free_sock_user_data(server, ps,
+                                     "Server is shutting down");
+    }
+    silc_packet_engine_free_streams_list(list);
+  }
 
-  case SILC_PACKET_CHANNEL_KEY:
-    /*
-     * Received key for channel. As channels are created by the router
-     * the keys are as well. We will distribute the key to all of our
-     * locally connected clients on the particular channel. Router
-     * never receives this channel and thus is ignored.
-     */
-    SILC_LOG_DEBUG(("Channel Key packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_channel_key(server, sock, packet);
-    break;
+  /* We are not connected to network anymore */
+  server->standalone = TRUE;
 
-    /*
-     * Command packets
-     */
-  case SILC_PACKET_COMMAND:
-    /*
-     * Recived command. Processes the command request and allocates the
-     * command context and calls the command.
-     */
-    SILC_LOG_DEBUG(("Command packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_command_process(server, sock, packet);
-    break;
+  silc_dlist_start(server->listeners);
+  while ((listener = silc_dlist_get(server->listeners)))
+    silc_net_close_listener(listener);
 
-  case SILC_PACKET_COMMAND_REPLY:
-    /*
-     * Received command reply packet. Received command reply to command. It
-     * may be reply to command sent by us or reply to command sent by client
-     * that we've routed further.
-     */
-    SILC_LOG_DEBUG(("Command Reply packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_command_reply(server, sock, packet);
-    break;
+  silc_server_http_uninit(server);
 
-    /*
-     * Private Message packets
-     */
-  case SILC_PACKET_PRIVATE_MESSAGE:
-    /*
-     * Received private message packet. The packet is coming from either
-     * client or server.
-     */
-    SILC_LOG_DEBUG(("Private Message packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_private_message(server, sock, packet);
-    break;
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router_retry);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router);
 
-  case SILC_PACKET_PRIVATE_MESSAGE_KEY:
-    /*
-     * Private message key packet.
-     */
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_private_message_key(server, sock, packet);
-    break;
+  silc_schedule_stop(server->schedule);
 
-    /*
-     * Key Exchange protocol packets
-     */
-  case SILC_PACKET_KEY_EXCHANGE:
-    SILC_LOG_DEBUG(("KE packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
+  SILC_LOG_DEBUG(("Server stopped"));
+}
 
-    if (sock->protocol && sock->protocol->protocol &&
-       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+/* Purge expired client entries from the server */
 
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
+{
+  SilcServer server = context;
+  SilcClientEntry client;
+  SilcIDList id_list;
+  SilcUInt64 curtime = silc_time();
 
-      proto_ctx->packet = silc_packet_context_dup(packet);
+  SILC_LOG_DEBUG(("Expire timeout"));
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 100000);
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
-                     "protocol active, packet dropped."));
+  silc_dlist_start(server->expired_clients);
+  while ((client = silc_dlist_get(server->expired_clients))) {
+    /* For unregistered clients the created timestamp is actually
+       unregistered timestamp.  Make sure client remains in history
+       at least 500 seconds. */
+    if (client->data.created && curtime - client->data.created < 500)
+      continue;
 
-      /* XXX Trigger KE protocol?? Rekey actually, maybe. */
-    }
-    break;
+    id_list = (client->data.status & SILC_IDLIST_STATUS_LOCAL ?
+              server->local_list : server->global_list);
 
-  case SILC_PACKET_KEY_EXCHANGE_1:
-    SILC_LOG_DEBUG(("KE 1 packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
+    silc_idlist_del_data(client);
+    silc_idlist_del_client(id_list, client);
+    silc_dlist_del(server->expired_clients, client);
+  }
 
-    if (sock->protocol && sock->protocol->protocol &&
-       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
-        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
-
-      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);
-
-       /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock, 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;
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_purge_expired_clients, server,
+                                120, 0);
+}
 
-       /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock,
-                               0, 100000);
-      }
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
-                     "protocol active, packet dropped."));
-    }
-    break;
 
-  case SILC_PACKET_KEY_EXCHANGE_2:
-    SILC_LOG_DEBUG(("KE 2 packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
+/******************************* Connecting *********************************/
 
-    if (sock->protocol && sock->protocol->protocol &&
-       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
-        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
-
-      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);
-
-       /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock, 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;
+/* Free connection context */
+
+void silc_server_connection_free(SilcServerConnection sconn)
+{
+  if (!sconn)
+    return;
+  SILC_LOG_DEBUG(("Free connection %p", sconn));
+  silc_dlist_del(sconn->server->conns, sconn);
+  silc_server_config_unref(&sconn->conn);
+  silc_free(sconn->remote_host);
+  silc_free(sconn->backup_replace_ip);
+  silc_free(sconn);
+}
+
+/* Creates connection to a remote router. */
+
+void silc_server_create_connection(SilcServer server,
+                                  SilcBool reconnect,
+                                  SilcBool dynamic,
+                                  const char *remote_host, SilcUInt32 port,
+                                  SilcServerConnectCallback callback,
+                                  void *context)
+{
+  SilcServerConnection sconn;
+
+  /* Allocate connection object for hold connection specific stuff. */
+  sconn = silc_calloc(1, sizeof(*sconn));
+  if (!sconn)
+    return;
+  sconn->remote_host = strdup(remote_host);
+  sconn->remote_port = port;
+  sconn->no_reconnect = reconnect == FALSE;
+  sconn->callback = callback;
+  sconn->callback_context = context;
+  sconn->no_conf = dynamic;
+  sconn->server = server;
+
+  SILC_LOG_DEBUG(("Created connection %p", sconn));
+
+  silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
+                                sconn, 0, 0);
+}
+
+/* Connection authentication completion callback */
+
+static void
+silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
+                         void *context)
+{
+  SilcServerConnection sconn = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sconn->sock);
+  SilcServer server = entry->server;
+  SilcServerConfigServer *conn;
+  SilcServerConfigConnParams *param;
+  SilcIDListData idata;
+  SilcServerEntry id_entry = NULL;
+  unsigned char id[32];
+  SilcUInt32 id_len;
+  SilcID remote_id;
+  const char *ip;
+
+  SILC_LOG_DEBUG(("Connection %p authentication completed, entry %p",
+                 sconn, entry));
+
+  entry->op = NULL;
+
+  if (success == FALSE) {
+    /* Authentication failed */
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
+    silc_server_disconnect_remote(server, sconn->sock,
+                                 SILC_STATUS_ERR_AUTH_FAILED, NULL);
+    return;
+  }
+
+  /* XXX For now remote is router always */
+  entry->data.conn_type = SILC_CONN_ROUTER;
+
+  SILC_LOG_INFO(("Connected to %s %s",
+                SILC_CONNTYPE_STRING(entry->data.conn_type),
+                sconn->remote_host));
+
+  /* Create the actual entry for remote entity */
+  switch (entry->data.conn_type) {
+  case SILC_CONN_SERVER:
+    SILC_LOG_DEBUG(("Remote is SILC server"));
+
+    /* Add new server.  The server must register itself to us before it
+       becomes registered to SILC network. */
+    id_entry = silc_idlist_add_server(server->local_list,
+                                     strdup(sconn->remote_host),
+                                     SILC_SERVER, NULL, NULL, sconn->sock);
+    if (!id_entry) {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_free_sock_user_data(server, sconn->sock, NULL);
+      silc_server_disconnect_remote(server, sconn->sock,
+                                   SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+      return;
+    }
+
+    /* Statistics */
+    server->stat.my_servers++;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.servers++;
+    SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+
+    silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+    break;
+
+  case SILC_CONN_ROUTER:
+    SILC_LOG_DEBUG(("Remote is SILC router"));
+
+    /* Register to network */
+    silc_id_id2str(server->id, SILC_ID_SERVER, id, sizeof(id), &id_len);
+    if (!silc_packet_send_va(sconn->sock, SILC_PACKET_NEW_SERVER, 0,
+                            SILC_STR_UI_SHORT(id_len),
+                            SILC_STR_DATA(id, id_len),
+                            SILC_STR_UI_SHORT(strlen(server->server_name)),
+                            SILC_STR_DATA(server->server_name,
+                                          strlen(server->server_name)),
+                            SILC_STR_END)) {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_free_sock_user_data(server, sconn->sock, NULL);
+      silc_server_disconnect_remote(server, sconn->sock,
+                                   SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+      return;
+    }
 
-       /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock,
-                               0, 100000);
+    /* Get remote ID */
+    silc_packet_get_ids(sconn->sock, NULL, NULL, NULL, &remote_id);
+
+    /* Check that we do not have this ID already */
+    id_entry = silc_idlist_find_server_by_id(server->local_list,
+                                            &remote_id.u.server_id,
+                                            TRUE, NULL);
+    if (id_entry) {
+      silc_idcache_del_by_context(server->local_list->servers, id_entry, NULL);
+    } else {
+      id_entry = silc_idlist_find_server_by_id(server->global_list,
+                                              &remote_id.u.server_id,
+                                              TRUE, NULL);
+      if (id_entry)
+       silc_idcache_del_by_context(server->global_list->servers, id_entry,
+                                   NULL);
+    }
+
+    SILC_LOG_DEBUG(("New server id(%s)",
+                   silc_id_render(&remote_id.u.server_id, SILC_ID_SERVER)));
+
+    /* Add the connected router to global server list.  Router is sent
+       as NULL since it's local to us. */
+    id_entry = silc_idlist_add_server(server->global_list,
+                                     strdup(sconn->remote_host),
+                                     SILC_ROUTER,
+                                     silc_id_dup(&remote_id.u.server_id,
+                                                 SILC_ID_SERVER),
+                                     NULL, sconn->sock);
+    if (!id_entry) {
+      /* Try reconnecting if configuration wants it */
+      if (!sconn->no_reconnect) {
+        silc_schedule_task_add_timeout(server->schedule,
+                                      silc_server_connect_to_router_retry,
+                                      sconn, 1, 0);
+        silc_dlist_del(server->conns, sconn);
+        return;
+      }
+
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_free_sock_user_data(server, sconn->sock, NULL);
+      silc_server_disconnect_remote(server, sconn->sock,
+                                   SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+      return;
+    }
+
+    /* Registered */
+    silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+    idata = (SilcIDListData)id_entry;
+    idata->status |= (SILC_IDLIST_STATUS_REGISTERED |
+                     SILC_IDLIST_STATUS_LOCAL);
+    idata->sconn = sconn;
+
+    /* Statistics */
+    server->stat.my_routers++;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.routers++;
+    SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+
+    if (!sconn->backup) {
+      /* Mark this router our primary router if we're still standalone */
+      if (server->standalone) {
+       SILC_LOG_DEBUG(("This connection is our primary router"));
+       server->id_entry->router = id_entry;
+       server->router = id_entry;
+       server->router->server_type = SILC_ROUTER;
+       server->standalone = FALSE;
+       server->backup_primary = FALSE;
+
+       /* Announce data if we are not backup router (unless not as primary
+          currently).  Backup router announces later at the end of
+          resuming protocol. */
+       if (server->backup_router && server->server_type == SILC_ROUTER) {
+         SILC_LOG_DEBUG(("Announce data after resume protocol"));
+       } else {
+         /* If we are router then announce our possible servers.  Backup
+            router announces also global servers. */
+         if (server->server_type == SILC_ROUTER)
+           silc_server_announce_servers(server,
+                                        server->backup_router ? TRUE : FALSE,
+                                        0, SILC_PRIMARY_ROUTE(server));
+
+         /* Announce our clients and channels to the router */
+         silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
+         silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+       }
+
+       /* If we are backup router then this primary router is whom we are
+          backing up. */
+       if (server->server_type == SILC_BACKUP_ROUTER) {
+         silc_socket_stream_get_info(silc_packet_stream_get_stream(sconn->
+                                                                   sock),
+                                     NULL, NULL, &ip, NULL);
+         silc_server_backup_add(server, server->id_entry, ip,
+                                sconn->remote_port, TRUE);
+       }
+      }
+#if 0
+         else {
+       /* We already have primary router.  Disconnect this connection */
+       SILC_LOG_DEBUG(("We already have primary router, disconnect"));
+       silc_idlist_del_server(server->global_list, id_entry);
+       if (sconn->callback)
+         (*sconn->callback)(server, NULL, sconn->callback_context);
+       silc_server_free_sock_user_data(server, sconn->sock, NULL);
+       silc_server_disconnect_remote(server, sconn->sock,
+                                     SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+       return;
       }
+#endif /* 0 */
     } else {
-      SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
-                     "protocol active, packet dropped."));
+      /* Add this server to be our backup router */
+      id_entry->server_type = SILC_BACKUP_ROUTER;
+      silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
+                            sconn->backup_replace_port, FALSE);
     }
-    break;
 
-  case SILC_PACKET_CONNECTION_AUTH_REQUEST:
-    /*
-     * Connection authentication request packet. When we receive this packet
-     * we will send to the other end information about our mandatory
-     * authentication method for the connection. This packet maybe received
-     * at any time. 
-     */
-    SILC_LOG_DEBUG(("Connection authentication request packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_connection_auth_request(server, sock, packet);
     break;
 
-    /*
-     * Connection Authentication protocol packets
-     */
-  case SILC_PACKET_CONNECTION_AUTH:
-    /* Start of the authentication protocol. We receive here the 
-       authentication data and will verify it. */
-    SILC_LOG_DEBUG(("Connection auth packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
+  default:
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
+    silc_server_disconnect_remote(server, sconn->sock,
+                                 SILC_STATUS_ERR_AUTH_FAILED, NULL);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Connection established, sock %p", sconn->sock));
+
+  conn = sconn->conn.ref_ptr;
+  param = &server->config->param;
+  if (conn && conn->param)
+    param = conn->param;
+
+  /* Register rekey timeout */
+  sconn->rekey_timeout = param->key_exchange_rekey;
+  silc_schedule_task_add_timeout(server->schedule, silc_server_do_rekey,
+                                sconn->sock, sconn->rekey_timeout, 0);
+
+  /* Set the entry as packet stream context */
+  silc_packet_set_context(sconn->sock, id_entry);
+
+  /* Call the completion callback to indicate that we've connected to
+     the router */
+  if (sconn && sconn->callback)
+    (*sconn->callback)(server, id_entry, sconn->callback_context);
+
+  if (sconn == server->router_conn)
+    server->router_conn = NULL;
+
+  silc_free(entry);
+}
+
+/* SKE completion callback */
+
+static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
+                                    SilcSKESecurityProperties prop,
+                                    SilcSKEKeyMaterial keymat,
+                                    SilcSKERekeyMaterial rekey,
+                                    void *context)
+{
+  SilcPacketStream sock = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sock);
+  SilcServerConnection sconn = entry->data.sconn;
+  SilcServer server = entry->server;
+  SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+  SilcAuthMethod auth_meth = SILC_AUTH_NONE;
+  void *auth_data = NULL;
+  SilcUInt32 auth_data_len = 0;
+  SilcConnAuth connauth;
+  SilcCipher send_key, receive_key;
+  SilcHmac hmac_send, hmac_receive;
+  SilcHash hash;
+
+  SILC_LOG_DEBUG(("Connection %p, SKE completed, entry %p", sconn, entry));
+
+  entry->op = NULL;
+
+  if (status != SILC_SKE_STATUS_OK) {
+    /* SKE failed */
+    SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+                   silc_ske_map_status(status), entry->hostname, entry->ip));
+    silc_ske_free(ske);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
+    silc_server_disconnect_remote(server, sconn->sock,
+                                 SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Setting keys into use"));
+
+  /* Set the keys into use.  The data will be encrypted after this. */
+  if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
+                        &hmac_send, &hmac_receive, &hash)) {
+    silc_ske_free(ske);
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
 
-      SilcServerConnAuthInternalContext *proto_ctx = 
-       (SilcServerConnAuthInternalContext *)sock->protocol->context;
+    /* Error setting keys */
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
+    silc_server_disconnect_remote(server, sconn->sock,
+                                 SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    return;
+  }
+  silc_packet_set_keys(sconn->sock, send_key, receive_key, hmac_send,
+                      hmac_receive, FALSE);
+
+  SILC_LOG_DEBUG(("Starting connection authentication"));
+
+  connauth = silc_connauth_alloc(server->schedule, ske,
+                                server->config->conn_auth_timeout);
+  if (!connauth) {
+    silc_ske_free(ske);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
 
-      proto_ctx->packet = silc_packet_context_dup(packet);
+    /** Error allocating auth protocol */
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
+    silc_server_disconnect_remote(server, sconn->sock,
+                                 SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    return;
+  }
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 0);
+  /* Get authentication method */
+  if (conn) {
+    if (conn->passphrase) {
+      if (conn->publickeys && !server->config->prefer_passphrase_auth) {
+       auth_meth = SILC_AUTH_PUBLIC_KEY;
+       auth_data = server->private_key;
+      } else {
+       auth_meth = SILC_AUTH_PASSWORD;
+       auth_data = conn->passphrase;
+       auth_data_len = conn->passphrase_len;
+      }
     } else {
-      SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
-                     "protocol active, packet dropped."));
+      auth_meth = SILC_AUTH_PUBLIC_KEY;
+      auth_data = server->private_key;
     }
-    break;
+  }
 
-  case SILC_PACKET_NEW_ID:
-    /*
-     * Received New ID packet. This includes some new ID that has been
-     * created. It may be for client, server or channel. This is the way
-     * to distribute information about new registered entities in the
-     * SILC network.
-     */
-    SILC_LOG_DEBUG(("New ID packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      silc_server_new_id_list(server, sock, packet);
-    else
-      silc_server_new_id(server, sock, packet);
-    break;
+  entry->data.rekey = rekey;
 
-  case SILC_PACKET_NEW_CLIENT:
-    /*
-     * Received new client packet. This includes client information that
-     * we will use to create initial client ID. After creating new
-     * ID we will send it to the client.
-     */
-    SILC_LOG_DEBUG(("New Client packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_new_client(server, sock, packet);
-    break;
+  /* Start connection authentication */
+  entry->op =
+    silc_connauth_initiator(connauth, server->server_type == SILC_SERVER ?
+                           SILC_CONN_SERVER : SILC_CONN_ROUTER, auth_meth,
+                           auth_data, auth_data_len,
+                           silc_server_ke_auth_compl, sconn);
+}
 
-  case SILC_PACKET_NEW_SERVER:
-    /*
-     * Received new server packet. This includes Server ID and some other
-     * information that we may save. This is received after server has 
-     * connected to us.
-     */
-    SILC_LOG_DEBUG(("New Server packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_new_server(server, sock, packet);
-    break;
+/* 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. */
 
-  case SILC_PACKET_NEW_CHANNEL:
-    /*
-     * Received new channel packet. Information about new channel in the
-     * network are distributed using this packet.
-     */
-    SILC_LOG_DEBUG(("New Channel packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      silc_server_new_channel_list(server, sock, packet);
-    else
-      silc_server_new_channel(server, sock, packet);
-    break;
+void silc_server_start_key_exchange(SilcServerConnection sconn)
+{
+  SilcServer server = sconn->server;
+  SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+  SilcUnknownEntry entry;
+  SilcSKEParamsStruct params;
+  SilcSKE ske;
+
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_context(server->schedule, sconn);
+
+  /* Create packet stream */
+  sconn->sock = silc_packet_stream_create(server->packet_engine,
+                                         server->schedule, sconn->stream);
+  if (!sconn->sock) {
+    SILC_LOG_ERROR(("Cannot connect: cannot create packet stream"));
+    silc_stream_destroy(sconn->stream);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
 
-  case SILC_PACKET_HEARTBEAT:
-    /*
-     * Received heartbeat.
-     */
-    SILC_LOG_DEBUG(("Heartbeat packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_connection_free(sconn);
+    return;
+  }
+  server->stat.conn_num++;
+
+  /* Set source ID to packet stream */
+  if (!silc_packet_set_ids(sconn->sock, SILC_ID_SERVER, server->id,
+                          0, NULL)) {
+    silc_packet_stream_destroy(sconn->sock);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_connection_free(sconn);
+    return;
+  }
+
+  /* Create entry for remote entity */
+  entry = silc_calloc(1, sizeof(*entry));
+  if (!entry) {
+    silc_packet_stream_destroy(sconn->sock);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_connection_free(sconn);
+    return;
+  }
+  entry->server = server;
+  entry->data.sconn = sconn;
+  silc_packet_set_context(sconn->sock, entry);
+
+  SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
+  /* Set Key Exchange flags from configuration, but fall back to global
+     settings too. */
+  memset(&params, 0, sizeof(params));
+  SILC_GET_SKE_FLAGS(conn, params.flags);
+  if (server->config->param.key_exchange_pfs)
+    params.flags |= SILC_SKE_SP_FLAG_PFS;
+
+  /* Start SILC Key Exchange protocol */
+  SILC_LOG_DEBUG(("Starting key exchange protocol, connection %p", sconn));
+  ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
+                      server->public_key, server->private_key, sconn);
+  if (!ske) {
+    silc_free(entry);
+    silc_packet_stream_destroy(sconn->sock);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_connection_free(sconn);
+    return;
+  }
+  silc_ske_set_callbacks(ske, silc_server_verify_key,
+                        silc_server_ke_completed, sconn->sock);
+
+  /* Start key exchange protocol */
+  params.version = silc_version_string;
+  params.timeout_secs = server->config->key_exchange_timeout;
+  entry->op = silc_ske_initiator(ske, sconn->sock, &params, NULL);
+}
+
+/* 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 = context;
+  SilcServer server = sconn->server;
+  SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+  SilcServerConfigConnParams *param =
+               (conn->param ? conn->param : &server->config->param);
+
+  SILC_LOG_INFO(("Retrying connecting to %s:%d", sconn->remote_host,
+                sconn->remote_port));
+
+  /* Calculate next timeout */
+  if (sconn->retry_count >= 1) {
+    sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+    if (sconn->retry_timeout > param->reconnect_interval_max)
+      sconn->retry_timeout = param->reconnect_interval_max;
+  } else {
+    sconn->retry_timeout = param->reconnect_interval;
+  }
+  sconn->retry_count++;
+  sconn->retry_timeout = sconn->retry_timeout +
+    (silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER);
+
+  /* If we've reached max retry count, give up. */
+  if ((sconn->retry_count > param->reconnect_count) &&
+      sconn->no_reconnect) {
+    SILC_LOG_ERROR(("Could not connect, giving up"));
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_connection_free(sconn);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Retrying connecting %d seconds", sconn->retry_timeout));
+
+  /* We will lookup a fresh pointer later */
+  silc_server_config_unref(&sconn->conn);
+
+  /* Wait before retrying */
+  silc_schedule_task_del_by_context(server->schedule, sconn);
+  silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
+                                sconn, sconn->retry_timeout, 0);
+}
+
+/* Callback for async connection to remote router */
+
+static void silc_server_connection_established(SilcNetStatus status,
+                                              SilcStream stream,
+                                              void *context)
+{
+  SilcServerConnection sconn = context;
+  SilcServer server = sconn->server;
+
+  silc_schedule_task_del_by_context(server->schedule, sconn);
+  sconn->op = NULL;
+
+  switch (status) {
+  case SILC_NET_OK:
+    SILC_LOG_DEBUG(("Connection %p to %s:%d established", sconn,
+                   sconn->remote_host, sconn->remote_port));
+
+    /* Continue with key exchange protocol */
+    sconn->stream = stream;
+    silc_server_start_key_exchange(sconn);
     break;
 
-  case SILC_PACKET_KEY_AGREEMENT:
-    /*
-     * Received heartbeat.
-     */
-    SILC_LOG_DEBUG(("Key agreement packet"));
-    if (packet->flags & SILC_PACKET_FLAG_LIST)
-      break;
-    silc_server_key_agreement(server, sock, packet);
+  case SILC_NET_UNKNOWN_IP:
+  case SILC_NET_UNKNOWN_HOST:
+    SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
+                   sconn->remote_host, sconn->remote_port,
+                   silc_net_get_error_string(status)));
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(sconn->server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+    } else {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_connection_free(sconn);
+    }
     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);
+  default:
+    SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
+                   sconn->remote_host, sconn->remote_port,
+                   silc_net_get_error_string(status)));
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(sconn->server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+    } else {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_connection_free(sconn);
+    }
     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;
+/* Generic routine to use connect to a router. */
 
-    if (sock->protocol && sock->protocol->protocol &&
-       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{
+  SilcServerConnection sconn = context;
+  SilcServer server = sconn->server;
+  SilcServerConfigRouter *rconn;
 
-      SilcServerRekeyInternalContext *proto_ctx = 
-       (SilcServerRekeyInternalContext *)sock->protocol->context;
+  silc_schedule_task_del_by_context(server->schedule, sconn);
 
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown) {
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_connection_free(sconn);
+    return;
+  }
 
-      proto_ctx->packet = silc_packet_context_dup(packet);
+  SILC_LOG_INFO(("Connecting to the %s %s on port %d",
+                (sconn->backup ? "backup router" : "router"),
+                sconn->remote_host, sconn->remote_port));
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 0);
-    } else {
-      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
-                     "protocol active, packet dropped."));
+  if (!sconn->no_conf) {
+    /* Find connection configuration */
+    rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
+                                               sconn->remote_port);
+    if (!rconn) {
+      SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
+                    (sconn->backup ? "backup router" : "router"),
+                    sconn->remote_host, sconn->remote_port));
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_connection_free(sconn);
+      return;
     }
+    silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
+  }
+
+  /* Connect to remote host */
+  sconn->op =
+    silc_net_tcp_connect((!server->config->server_info->primary ? NULL :
+                         server->config->server_info->primary->server_ip),
+                        sconn->remote_host, sconn->remote_port,
+                        server->schedule, silc_server_connection_established,
+                        sconn);
+  if (!sconn->op) {
+    SILC_LOG_ERROR(("Could not connect to router %s:%d",
+                   sconn->remote_host, sconn->remote_port));
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_connection_free(sconn);
+    return;
+  }
+
+  /* Add to connection list */
+  silc_dlist_add(server->conns, sconn);
+}
+
+/* This function connects to our primary router or if we are a router this
+   establishes all our primary routes. This is called at the start of the
+   server to do authentication and key exchange with our router - called
+   from schedule. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router)
+{
+  SilcServer server = context;
+  SilcServerConnection sconn;
+  SilcServerConfigRouter *ptr;
+  SilcServerConfigConnParams *param;
+
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown)
+    return;
+
+  SILC_LOG_DEBUG(("We are %s",
+                 (server->server_type == SILC_SERVER ?
+                  "normal server" : server->server_type == SILC_ROUTER ?
+                  "router" : "backup router/normal server")));
+
+  if (!server->config->routers) {
+    /* There wasn't a configured router, we will continue but we don't
+       have a connection to outside world.  We will be standalone server. */
+    SILC_LOG_DEBUG(("No router(s), we are standalone"));
+    server->standalone = TRUE;
+    return;
+  }
+
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router_retry);
+
+  /* Create the connections to all our routes */
+  for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+
+    SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+                   ptr->backup_router ? "Backup router" : "Router",
+                   ptr->initiator ? "Initiator" : "Responder",
+                   ptr->host, ptr->port));
+
+    if (server->server_type == SILC_ROUTER && ptr->backup_router &&
+       ptr->initiator == FALSE && !server->backup_router &&
+       !silc_server_config_get_backup_router(server))
+      server->wait_backup = TRUE;
+
+    if (!ptr->initiator)
+      continue;
+    if (ptr->dynamic_connection)
+      continue;
+
+    /* Check whether we are connecting or connected to this host already */
+    if (silc_server_num_sockets_by_remote(server,
+                                         silc_net_is_ip(ptr->host) ?
+                                         ptr->host : NULL,
+                                         silc_net_is_ip(ptr->host) ?
+                                         NULL : ptr->host, ptr->port,
+                                         SILC_CONN_ROUTER)) {
+      SILC_LOG_DEBUG(("We are already connected to %s:%d",
+                     ptr->host, ptr->port));
+
+      /* If we don't have primary router and this connection is our
+        primary router we are in desync.  Reconnect to the primary. */
+      if (server->standalone && !server->router) {
+       /* XXX */
+       SilcPacketStream sock;
+       SilcServerConfigRouter *primary =
+         silc_server_config_get_primary_router(server);
+       if (primary != ptr)
+         continue;
+       sock = silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+                                              ptr->host, ptr->port);
+       if (!sock)
+         continue;
+       server->backup_noswitch = TRUE;
+       silc_server_free_sock_user_data(server, sock, NULL);
+       silc_server_disconnect_remote(server, sock, 0, NULL);
+       server->backup_noswitch = FALSE;
+       SILC_LOG_DEBUG(("Reconnecting to primary router"));
+      } else {
+       continue;
+      }
+    }
+
+    param = (ptr->param ? ptr->param : &server->config->param);
+
+    /* Allocate connection object for hold connection specific stuff. */
+    sconn = silc_calloc(1, sizeof(*sconn));
+    if (!sconn)
+      continue;
+    sconn->server = server;
+    sconn->remote_host = strdup(ptr->host);
+    sconn->remote_port = ptr->port;
+    sconn->backup = ptr->backup_router;
+    if (sconn->backup) {
+      sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
+      sconn->backup_replace_port = ptr->backup_replace_port;
+    }
+    sconn->no_reconnect = param->reconnect_keep_trying == FALSE;
+
+    SILC_LOG_DEBUG(("Created connection %p", sconn));
+
+    if (!server->router_conn && !sconn->backup)
+      server->router_conn = sconn;
+
+    /* Connect */
+    silc_server_connect_router(server->schedule, server, SILC_TASK_EXPIRE,
+                              0, sconn);
+  }
+}
+
+
+/************************ Accepting new connection **************************/
+
+/* After this is called, server don't wait for backup router anymore.
+   This gets called automatically even after we have backup router
+   connection established. */
+
+SILC_TASK_CALLBACK(silc_server_backup_router_wait)
+{
+  SilcServer server = context;
+  server->wait_backup = FALSE;
+}
+
+/* Authentication data callback */
+
+static SilcBool
+silc_server_accept_get_auth(SilcConnAuth connauth,
+                           SilcConnectionType conn_type,
+                           unsigned char **passphrase,
+                           SilcUInt32 *passphrase_len,
+                           SilcSKR *repository,
+                           void *context)
+{
+  SilcPacketStream sock = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sock);
+  SilcServer server = entry->server;
+
+  SILC_LOG_DEBUG(("Remote connection type %d", conn_type));
+
+  /* Remote end is client */
+  if (conn_type == SILC_CONN_CLIENT) {
+    SilcServerConfigClient *cconfig = entry->cconfig.ref_ptr;
+    if (!cconfig)
+      return FALSE;
+
+    *passphrase = cconfig->passphrase;
+    *passphrase_len = cconfig->passphrase_len;
+    if (cconfig->publickeys)
+      *repository = server->repository;
+
+    if (cconfig->publickeys) {
+      if (server->config->prefer_passphrase_auth) {
+       *repository = NULL;
+      } else {
+       *passphrase = NULL;
+       *passphrase_len = 0;
+      }
+    }
+
+    entry->conn_type = conn_type;
+    return TRUE;
+  }
+
+  /* Remote end is server */
+  if (conn_type == SILC_CONN_SERVER) {
+    SilcServerConfigServer *sconfig;
+
+    /* If we are normal server, don't accept the connection */
+    if (server->server_type == SILC_SERVER)
+      return FALSE;
+
+    sconfig = entry->sconfig.ref_ptr;
+    if (!sconfig)
+      return FALSE;
+
+    *passphrase = sconfig->passphrase;
+    *passphrase_len = sconfig->passphrase_len;
+    if (sconfig->publickeys)
+      *repository = server->repository;
+
+    if (sconfig->publickeys) {
+      if (server->config->prefer_passphrase_auth) {
+       *repository = NULL;
+      } else {
+       *passphrase = NULL;
+       *passphrase_len = 0;
+      }
+    }
+
+    entry->conn_type = conn_type;
+    return TRUE;
+  }
+
+  /* Remote end is router */
+  if (conn_type == SILC_CONN_ROUTER) {
+    SilcServerConfigRouter *rconfig = entry->rconfig.ref_ptr;
+    if (!rconfig)
+      return FALSE;
+
+    *passphrase = rconfig->passphrase;
+    *passphrase_len = rconfig->passphrase_len;
+    if (rconfig->publickeys)
+      *repository = server->repository;
+
+    if (rconfig->publickeys) {
+      if (server->config->prefer_passphrase_auth) {
+       *repository = NULL;
+      } else {
+       *passphrase = NULL;
+       *passphrase_len = 0;
+      }
+    }
+
+    entry->conn_type = conn_type;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Authentication completion callback. */
+
+static void
+silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
+                             void *context)
+{
+  SilcPacketStream sock = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sock);
+  SilcIDListData idata = (SilcIDListData)entry;
+  SilcServer server = entry->server;
+  SilcServerConfigConnParams *param = &server->config->param;
+  SilcServerConnection sconn;
+  void *id_entry;
+  const char *hostname, *ip;
+  SilcUInt16 port;
+
+  entry->op = NULL;
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                             NULL, &hostname, &ip, &port);
+
+  if (success == FALSE) {
+    /* Authentication failed */
+    SILC_LOG_INFO(("Authentication failed for %s (%s) [%s]", entry->hostname,
+                  entry->ip, SILC_CONNTYPE_STRING(entry->data.conn_type)));
+    server->stat.auth_failures++;
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    silc_server_config_unref(&entry->cconfig);
+    silc_server_config_unref(&entry->sconfig);
+    silc_server_config_unref(&entry->rconfig);
+    silc_server_free_sock_user_data(server, sock, NULL);
+    goto out;
+  }
+
+  SILC_LOG_DEBUG(("Checking whether connection is allowed"));
+
+  switch (entry->conn_type) {
+  case SILC_CONN_CLIENT:
+    {
+      SilcClientEntry client;
+      SilcServerConfigClient *conn = entry->cconfig.ref_ptr;
+
+      /* Verify whether this connection is after all allowed to connect */
+      if (!silc_server_connection_allowed(server, sock, entry->conn_type,
+                                         &server->config->param,
+                                         conn->param,
+                                         silc_connauth_get_ske(connauth))) {
+       server->stat.auth_failures++;
+       goto out;
+      }
+
+      /* If we are primary router and we have backup router configured
+        but it has not connected to use yet, do not accept any other
+        connection. */
+      if (server->wait_backup && server->server_type == SILC_ROUTER &&
+         !server->backup_router) {
+       SilcServerConfigRouter *router;
+       router = silc_server_config_get_backup_router(server);
+       if (router && strcmp(server->config->server_info->primary->server_ip,
+                            entry->ip) &&
+           silc_server_find_socket_by_host(server,
+                                           SILC_CONN_SERVER,
+                                           router->backup_replace_ip, 0)) {
+         SILC_LOG_INFO(("Will not accept connections because we do "
+                        "not have backup router connection established"));
+         silc_server_disconnect_remote(server, sock,
+                                       SILC_STATUS_ERR_PERM_DENIED,
+                                       "We do not have connection to backup "
+                                       "router established, try later");
+         silc_server_config_unref(&entry->cconfig);
+         silc_server_config_unref(&entry->sconfig);
+         silc_server_config_unref(&entry->rconfig);
+         silc_server_free_sock_user_data(server, sock, NULL);
+         server->stat.auth_failures++;
+
+         /* From here on, wait 20 seconds for the backup router to appear. */
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_backup_router_wait,
+                                        (void *)server, 20, 0);
+         goto out;
+       }
+      }
+
+      SILC_LOG_DEBUG(("Remote host is client"));
+      SILC_LOG_INFO(("Connection %s (%s) is client", entry->hostname,
+                    entry->ip));
+
+      /* Add the client to the client ID cache. The nickname and Client ID
+        and other information is created after we have received NEW_CLIENT
+        packet from client. */
+      client = silc_idlist_add_client(server->local_list,
+                                     NULL, NULL, NULL, NULL, NULL, sock);
+      if (!client) {
+       SILC_LOG_ERROR(("Could not add new client to cache"));
+       server->stat.auth_failures++;
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_AUTH_FAILED, NULL);
+       silc_server_config_unref(&entry->cconfig);
+       silc_server_config_unref(&entry->sconfig);
+       silc_server_config_unref(&entry->rconfig);
+       silc_server_free_sock_user_data(server, sock, NULL);
+       goto out;
+      }
+      entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+      entry->data.conn_type = SILC_CONN_CLIENT;
+
+      /* Statistics */
+      server->stat.my_clients++;
+      server->stat.clients++;
+      server->stat.cell_clients++;
+
+      /* Get connection parameters */
+      if (conn->param) {
+       param = conn->param;
+
+       if (!param->keepalive_secs)
+         param->keepalive_secs = server->config->param.keepalive_secs;
+
+       if (!param->qos && server->config->param.qos) {
+         param->qos = server->config->param.qos;
+         param->qos_rate_limit = server->config->param.qos_rate_limit;
+         param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+         param->qos_limit_sec = server->config->param.qos_limit_sec;
+         param->qos_limit_usec = server->config->param.qos_limit_usec;
+       }
+
+       /* Check if to be anonymous connection */
+       if (param->anonymous)
+         client->mode |= SILC_UMODE_ANONYMOUS;
+      }
+
+      /* Add public key to repository */
+      SILC_LOG_DEBUG(("Add client public key to repository"));
+      if (!silc_server_get_public_key_by_client(server, client, NULL))
+       silc_skr_add_public_key_simple(server->repository,
+                                      entry->data.public_key,
+                                      SILC_SKR_USAGE_IDENTIFICATION, client,
+                                      NULL);
+
+      id_entry = (void *)client;
+      break;
+    }
+
+  case SILC_CONN_SERVER:
+  case SILC_CONN_ROUTER:
+    {
+      SilcServerEntry new_server;
+      SilcBool initiator = FALSE;
+      SilcBool backup_local = FALSE;
+      SilcBool backup_router = FALSE;
+      char *backup_replace_ip = NULL;
+      SilcUInt16 backup_replace_port = 0;
+      SilcServerConfigServer *srvconn = entry->sconfig.ref_ptr;
+      SilcServerConfigRouter *rconn = entry->rconfig.ref_ptr;
+
+      /* If we are backup router and this is incoming server connection
+        and we do not have connection to primary router, do not allow
+        the connection. */
+      if (server->server_type == SILC_BACKUP_ROUTER &&
+         entry->conn_type == SILC_CONN_SERVER &&
+         !SILC_PRIMARY_ROUTE(server)) {
+       SILC_LOG_INFO(("Will not accept server connection because we do "
+                      "not have primary router connection established"));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_PERM_DENIED,
+                                     "We do not have connection to primary "
+                                     "router established, try later");
+       silc_server_config_unref(&entry->cconfig);
+       silc_server_config_unref(&entry->sconfig);
+       silc_server_config_unref(&entry->rconfig);
+       silc_server_free_sock_user_data(server, sock, NULL);
+       server->stat.auth_failures++;
+       goto out;
+      }
+
+      if (entry->conn_type == SILC_CONN_ROUTER) {
+       /* Verify whether this connection is after all allowed to connect */
+       if (!silc_server_connection_allowed(server, sock,
+                                           entry->conn_type,
+                                           &server->config->param,
+                                           rconn ? rconn->param : NULL,
+                                           silc_connauth_get_ske(connauth))) {
+         silc_server_config_unref(&entry->cconfig);
+         silc_server_config_unref(&entry->sconfig);
+         silc_server_config_unref(&entry->rconfig);
+         server->stat.auth_failures++;
+         goto out;
+       }
+
+       if (rconn) {
+         if (rconn->param) {
+           param = rconn->param;
+
+           if (!param->keepalive_secs)
+             param->keepalive_secs = server->config->param.keepalive_secs;
+
+           if (!param->qos && server->config->param.qos) {
+             param->qos = server->config->param.qos;
+             param->qos_rate_limit = server->config->param.qos_rate_limit;
+             param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+             param->qos_limit_sec = server->config->param.qos_limit_sec;
+             param->qos_limit_usec = server->config->param.qos_limit_usec;
+           }
+         }
+
+         initiator = rconn->initiator;
+         backup_local = rconn->backup_local;
+         backup_router = rconn->backup_router;
+         backup_replace_ip = rconn->backup_replace_ip;
+         backup_replace_port = rconn->backup_replace_port;
+       }
+      }
+
+      if (entry->conn_type == SILC_CONN_SERVER) {
+       /* Verify whether this connection is after all allowed to connect */
+       if (!silc_server_connection_allowed(server, sock,
+                                           entry->conn_type,
+                                           &server->config->param,
+                                           srvconn ? srvconn->param : NULL,
+                                           silc_connauth_get_ske(connauth))) {
+         server->stat.auth_failures++;
+         goto out;
+       }
+       if (srvconn) {
+         if (srvconn->param) {
+           param = srvconn->param;
+
+           if (!param->keepalive_secs)
+             param->keepalive_secs = server->config->param.keepalive_secs;
+
+           if (!param->qos && server->config->param.qos) {
+             param->qos = server->config->param.qos;
+             param->qos_rate_limit = server->config->param.qos_rate_limit;
+             param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+             param->qos_limit_sec = server->config->param.qos_limit_sec;
+             param->qos_limit_usec = server->config->param.qos_limit_usec;
+           }
+         }
+
+         backup_router = srvconn->backup_router;
+       }
+      }
+
+      /* If we are primary router and we have backup router configured
+        but it has not connected to use yet, do not accept any other
+        connection. */
+      if (server->wait_backup && server->server_type == SILC_ROUTER &&
+         !server->backup_router && !backup_router) {
+       SilcServerConfigRouter *router;
+       router = silc_server_config_get_backup_router(server);
+       if (router && strcmp(server->config->server_info->primary->server_ip,
+                            ip) &&
+           silc_server_find_socket_by_host(server,
+                                           SILC_CONN_SERVER,
+                                           router->backup_replace_ip, 0)) {
+         SILC_LOG_INFO(("Will not accept connections because we do "
+                        "not have backup router connection established"));
+         silc_server_disconnect_remote(server, sock,
+                                       SILC_STATUS_ERR_PERM_DENIED,
+                                       "We do not have connection to backup "
+                                       "router established, try later");
+         silc_server_config_unref(&entry->cconfig);
+         silc_server_config_unref(&entry->sconfig);
+         silc_server_config_unref(&entry->rconfig);
+         silc_server_free_sock_user_data(server, sock, NULL);
+         server->stat.auth_failures++;
+
+         /* From here on, wait 20 seconds for the backup router to appear. */
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_backup_router_wait,
+                                        (void *)server, 20, 0);
+         goto out;
+       }
+      }
+
+      SILC_LOG_DEBUG(("Remote host is %s",
+                     entry->conn_type == SILC_CONN_SERVER ?
+                     "server" : (backup_router ?
+                                 "backup router" : "router")));
+      SILC_LOG_INFO(("Connection %s (%s) is %s", entry->hostname,
+                    entry->ip, entry->conn_type == SILC_CONN_SERVER ?
+                    "server" : (backup_router ?
+                                "backup router" : "router")));
+
+      /* Add the server into server cache. The server name and Server ID
+        is updated after we have received NEW_SERVER packet from the
+        server. We mark ourselves as router for this server if we really
+        are router. */
+      new_server =
+       silc_idlist_add_server((entry->conn_type == SILC_CONN_SERVER ?
+                               server->local_list : (backup_router ?
+                                                     server->local_list :
+                                                     server->global_list)),
+                              NULL,
+                              (entry->conn_type == SILC_CONN_SERVER ?
+                               SILC_SERVER : SILC_ROUTER),
+                              NULL,
+                              (entry->conn_type == SILC_CONN_SERVER ?
+                               server->id_entry : (backup_router ?
+                                                   server->id_entry : NULL)),
+                              sock);
+      if (!new_server) {
+       SILC_LOG_ERROR(("Could not add new server to cache"));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_AUTH_FAILED, NULL);
+       silc_server_config_unref(&entry->cconfig);
+       silc_server_config_unref(&entry->sconfig);
+       silc_server_config_unref(&entry->rconfig);
+       silc_server_free_sock_user_data(server, sock, NULL);
+       server->stat.auth_failures++;
+       goto out;
+      }
+      entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+      entry->data.conn_type = entry->conn_type;
+
+      id_entry = (void *)new_server;
+
+      /* If the incoming connection is router and marked as backup router
+        then add it to be one of our backups */
+      if (entry->data.conn_type == SILC_CONN_ROUTER && backup_router) {
+       /* Change it back to SERVER type since that's what it really is. */
+       if (backup_local)
+         entry->data.conn_type = SILC_CONN_SERVER;
+       new_server->server_type = SILC_BACKUP_ROUTER;
+
+       SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+                              ("Backup router %s is now online",
+                               entry->hostname));
+
+       /* Remove the backup waiting with timeout */
+       silc_schedule_task_add_timeout(server->schedule,
+                                      silc_server_backup_router_wait,
+                                      (void *)server, 10, 0);
+      }
+
+      /* Statistics */
+      if (entry->data.conn_type == SILC_CONN_SERVER) {
+       server->stat.my_servers++;
+       server->stat.servers++;
+       SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+      } else {
+       server->stat.my_routers++;
+       server->stat.routers++;
+       SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+      }
+
+      /* Check whether this connection is to be our primary router connection
+        if we do not already have the primary route. */
+      if (!backup_router &&
+         server->standalone && entry->data.conn_type == SILC_CONN_ROUTER) {
+       if (silc_server_config_is_primary_route(server) && !initiator)
+         break;
+
+       SILC_LOG_DEBUG(("We are not standalone server anymore"));
+       server->standalone = FALSE;
+       if (!server->id_entry->router) {
+         server->id_entry->router = id_entry;
+         server->router = id_entry;
+       }
+      }
+
+      break;
+    }
+
+  default:
+    goto out;
     break;
+  }
+
+  /* Add connection to server->conns so that we know we have connection
+     to this peer. */
+  sconn = silc_calloc(1, sizeof(*sconn));
+  sconn->server = server;
+  sconn->sock = sock;
+  sconn->remote_host = strdup(hostname);
+  sconn->remote_port = port;
+  silc_dlist_add(server->conns, sconn);
+  idata->sconn = sconn;
+  idata->last_receive = time(NULL);
+
+  /* Add the common data structure to the ID entry. */
+  silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+  silc_packet_set_context(sock, id_entry);
+
+  /* Connection has been fully established now. Everything is ok. */
+  SILC_LOG_DEBUG(("New connection %p authenticated", sconn));
+
+  /* Perform Quality of Service */
+  if (param->qos)
+    silc_socket_stream_set_qos(silc_packet_stream_get_stream(sock),
+                              param->qos_rate_limit, param->qos_bytes_limit,
+                              param->qos_limit_sec, param->qos_limit_usec);
+
+  silc_server_config_unref(&entry->cconfig);
+  silc_server_config_unref(&entry->sconfig);
+  silc_server_config_unref(&entry->rconfig);
+  silc_free(entry);
+
+ out:
+  silc_ske_free(silc_connauth_get_ske(connauth));
+  silc_connauth_free(connauth);
+}
+
+/* SKE completion callback.  We set the new keys into use here. */
+
+static void
+silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
+                            SilcSKESecurityProperties prop,
+                            SilcSKEKeyMaterial keymat,
+                            SilcSKERekeyMaterial rekey,
+                            void *context)
+{
+  SilcPacketStream sock = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sock);
+  SilcIDListData idata = (SilcIDListData)entry;
+  SilcServer server = entry->server;
+  SilcConnAuth connauth;
+  SilcCipher send_key, receive_key;
+  SilcHmac hmac_send, hmac_receive;
+  SilcHash hash;
+  unsigned char *pk;
+  SilcUInt32 pk_len;
+
+  entry->op = NULL;
+
+  if (status != SILC_SKE_STATUS_OK) {
+    /* SKE failed */
+    SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+                   silc_ske_map_status(status), entry->hostname, entry->ip));
+    silc_ske_free(ske);
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    silc_server_config_unref(&entry->cconfig);
+    silc_server_config_unref(&entry->sconfig);
+    silc_server_config_unref(&entry->rconfig);
+    silc_server_free_sock_user_data(server, sock, NULL);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Setting keys into use"));
+
+  /* Set the keys into use.  The data will be encrypted after this. */
+  if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
+                        &hmac_send, &hmac_receive, &hash)) {
+    /* Error setting keys */
+    silc_ske_free(ske);
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    silc_server_free_sock_user_data(server, sock, NULL);
+    return;
+  }
+  silc_packet_set_keys(sock, send_key, receive_key, hmac_send,
+                      hmac_receive, FALSE);
+
+  idata->rekey = rekey;
+  idata->public_key = silc_pkcs_public_key_copy(prop->public_key);
+  pk = silc_pkcs_public_key_encode(idata->public_key, &pk_len);
+  silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
+
+  silc_hash_alloc(silc_hash_get_name(prop->hash), &idata->hash);
+
+  SILC_LOG_DEBUG(("Starting connection authentication"));
+  server->stat.auth_attempts++;
+
+  connauth = silc_connauth_alloc(server->schedule, ske,
+                                server->config->conn_auth_timeout);
+  if (!connauth) {
+    /** Error allocating auth protocol */
+    silc_ske_free(ske);
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    silc_server_config_unref(&entry->cconfig);
+    silc_server_config_unref(&entry->sconfig);
+    silc_server_config_unref(&entry->rconfig);
+    silc_server_free_sock_user_data(server, sock, NULL);
+    return;
+  }
+
+  /* Start connection authentication */
+  entry->op =
+    silc_connauth_responder(connauth, silc_server_accept_get_auth,
+                           silc_server_accept_auth_compl, sock);
+}
+
+/* Accept new TCP connection */
+
+static void silc_server_accept_new_connection(SilcNetStatus status,
+                                             SilcStream stream,
+                                             void *context)
+{
+  SilcServer server = context;
+  SilcPacketStream packet_stream;
+  SilcServerConfigClient *cconfig = NULL;
+  SilcServerConfigServer *sconfig = NULL;
+  SilcServerConfigRouter *rconfig = NULL;
+  SilcServerConfigDeny *deny;
+  SilcUnknownEntry entry;
+  SilcSKE ske;
+  SilcSKEParamsStruct params;
+  char *hostname, *ip;
+  SilcUInt16 port;
+
+  SILC_LOG_DEBUG(("Accepting new connection"));
+
+  /* Check for maximum allowed connections */
+  server->stat.conn_attempts++;
+  if (silc_dlist_count(server->conns) >
+      server->config->param.connections_max) {
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
+    silc_stream_destroy(stream);
+    return;
+  }
+
+  /* Get hostname, IP and port */
+  if (!silc_socket_stream_get_info(stream, NULL, (const char **)&hostname,
+                                  (const char **)&ip, &port)) {
+    /* Bad socket stream */
+    server->stat.conn_failures++;
+    silc_stream_destroy(stream);
+    return;
+  }
+
+  /* Create packet stream */
+  packet_stream = silc_packet_stream_create(server->packet_engine,
+                                           server->schedule, stream);
+  if (!packet_stream) {
+    SILC_LOG_ERROR(("Refusing connection, cannot create packet stream"));
+    server->stat.conn_failures++;
+    silc_stream_destroy(stream);
+    return;
+  }
+  server->stat.conn_num++;
+
+  SILC_LOG_DEBUG(("Created packet stream %p", packet_stream));
+
+  /* Set source ID to packet stream */
+  if (!silc_packet_set_ids(packet_stream, SILC_ID_SERVER, server->id,
+                          0, NULL)) {
+    /* Out of memory */
+    server->stat.conn_failures++;
+    silc_packet_stream_destroy(packet_stream);
+    return;
+  }
+
+  /* Check whether this connection is denied to connect to us. */
+  deny = silc_server_config_find_denied(server, ip);
+  if (!deny)
+    deny = silc_server_config_find_denied(server, hostname);
+  if (deny) {
+    /* The connection is denied */
+    SILC_LOG_INFO(("Connection %s (%s) is denied", hostname, ip));
+    silc_server_disconnect_remote(server, packet_stream,
+                                 SILC_STATUS_ERR_BANNED_FROM_SERVER,
+                                 deny->reason);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
+    return;
+  }
+
+  /* Check whether we have configured this sort of connection at all. We
+     have to check all configurations since we don't know what type of
+     connection this is. */
+  if (!(cconfig = silc_server_config_find_client(server, ip)))
+    cconfig = silc_server_config_find_client(server, hostname);
+  if (!(sconfig = silc_server_config_find_server_conn(server, ip)))
+    sconfig = silc_server_config_find_server_conn(server, hostname);
+  if (server->server_type == SILC_ROUTER)
+    if (!(rconfig = silc_server_config_find_router_conn(server, ip, port)))
+      rconfig = silc_server_config_find_router_conn(server, hostname, port);
+  if (!cconfig && !sconfig && !rconfig) {
+    SILC_LOG_INFO(("Connection %s (%s) is not allowed", hostname, ip));
+    server->stat.conn_failures++;
+    silc_server_disconnect_remote(server, packet_stream,
+                                 SILC_STATUS_ERR_BANNED_FROM_SERVER, NULL);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
+    return;
+  }
+
+  /* The connection is allowed */
+  entry = silc_calloc(1, sizeof(*entry));
+  if (!entry) {
+    server->stat.conn_failures++;
+    silc_server_disconnect_remote(server, packet_stream,
+                                 SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
+    return;
+  }
+  entry->hostname = hostname;
+  entry->ip = ip;
+  entry->port = port;
+  entry->server = server;
+  entry->data.conn_type = SILC_CONN_UNKNOWN;
+  silc_packet_set_context(packet_stream, entry);
+
+  SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
+  silc_server_config_ref(&entry->cconfig, server->config, cconfig);
+  silc_server_config_ref(&entry->sconfig, server->config, sconfig);
+  silc_server_config_ref(&entry->rconfig, server->config, rconfig);
+
+  /* Take flags for key exchange. Since we do not know what type of connection
+     this is, we go through all found configurations and use the global ones
+     as well. This will result always into strictest key exchange flags. */
+  memset(&params, 0, sizeof(params));
+  SILC_GET_SKE_FLAGS(cconfig, params.flags);
+  SILC_GET_SKE_FLAGS(sconfig, params.flags);
+  SILC_GET_SKE_FLAGS(rconfig, params.flags);
+  if (server->config->param.key_exchange_pfs)
+    params.flags |= SILC_SKE_SP_FLAG_PFS;
+
+  SILC_LOG_INFO(("Incoming connection %s (%s)", hostname, ip));
+  server->stat.conn_attempts++;
+
+  /* Start SILC Key Exchange protocol */
+  SILC_LOG_DEBUG(("Starting key exchange protocol"));
+  ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
+                      server->public_key, server->private_key,
+                      packet_stream);
+  if (!ske) {
+    server->stat.conn_failures++;
+    silc_server_disconnect_remote(server, packet_stream,
+                                 SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
+    return;
+  }
+  silc_ske_set_callbacks(ske, silc_server_verify_key,
+                        silc_server_accept_completed, packet_stream);
+
+  /* Start key exchange protocol */
+  params.version = silc_version_string;
+  params.timeout_secs = server->config->key_exchange_timeout;
+  entry->op = silc_ske_responder(ske, packet_stream, &params);
+}
+
+
+/********************************** Rekey ***********************************/
+
+/* Initiator rekey completion callback */
+
+static void silc_server_rekey_completion(SilcSKE ske,
+                                        SilcSKEStatus status,
+                                        const SilcSKESecurityProperties prop,
+                                        const SilcSKEKeyMaterial keymat,
+                                        SilcSKERekeyMaterial rekey,
+                                        void *context)
+{
+  SilcPacketStream sock = context;
+  SilcIDListData idata = silc_packet_get_context(sock);
+  SilcServer server = idata->sconn->server;
+
+  idata->sconn->op = NULL;
+  if (status != SILC_SKE_STATUS_OK) {
+    SILC_LOG_ERROR(("Error during rekey protocol with %s",
+                   idata->sconn->remote_host));
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]",
+                 idata->sconn->remote_host, idata->sconn->remote_port,
+                 SILC_CONNTYPE_STRING(idata->conn_type)));
+
+  /* Save rekey data for next rekey */
+  idata->rekey = rekey;
+
+  /* Register new rekey timeout */
+  silc_schedule_task_add_timeout(server->schedule, silc_server_do_rekey,
+                                sock, idata->sconn->rekey_timeout, 0);
+}
+
+/* Rekey callback.  Start rekey as initiator */
+
+SILC_TASK_CALLBACK(silc_server_do_rekey)
+{
+  SilcServer server = app_context;
+  SilcPacketStream sock = context;
+  SilcIDListData idata = silc_packet_get_context(sock);
+  SilcSKE ske;
+
+  SILC_LOG_DEBUG(("Perform rekey, sock %p", sock));
+
+  /* Do not execute rekey with disabled connections */
+  if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
+
+  /* If another protocol is active do not start rekey */
+  if (idata->sconn->op) {
+    SILC_LOG_DEBUG(("Waiting for other protocol to finish before rekeying"));
+    silc_schedule_task_add_timeout(server->schedule, silc_server_do_rekey,
+                                  sock, 60, 0);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s]",
+                 idata->sconn->remote_host, idata->sconn->remote_port,
+                 SILC_CONNTYPE_STRING(idata->conn_type)));
+
+  /* Allocate SKE */
+  ske = silc_ske_alloc(server->rng, server->schedule, NULL,
+                      server->public_key, NULL, sock);
+  if (!ske)
+    return;
+
+  /* Set SKE callbacks */
+  silc_ske_set_callbacks(ske, NULL, silc_server_rekey_completion, sock);
+
+  /* Perform rekey */
+  idata->sconn->op = silc_ske_rekey_initiator(ske, sock, idata->rekey);
+}
+
+/* Responder rekey completion callback */
 
-  default:
-    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
-    break;
+static void
+silc_server_rekey_resp_completion(SilcSKE ske,
+                                 SilcSKEStatus status,
+                                 const SilcSKESecurityProperties prop,
+                                 const SilcSKEKeyMaterial keymat,
+                                 SilcSKERekeyMaterial rekey,
+                                 void *context)
+{
+  SilcPacketStream sock = context;
+  SilcIDListData idata = silc_packet_get_context(sock);
+
+  idata->sconn->op = NULL;
+  if (status != SILC_SKE_STATUS_OK) {
+    SILC_LOG_ERROR(("Error during rekey protocol with %s",
+                   idata->sconn->remote_host));
+    return;
   }
-  
+
+  SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]",
+                 idata->sconn->remote_host, idata->sconn->remote_port,
+                 SILC_CONNTYPE_STRING(idata->conn_type)));
+
+  /* Save rekey data for next rekey */
+  idata->rekey = rekey;
 }
 
-/* Creates connection to a remote router. */
+/* Start rekey as responder */
 
-void silc_server_create_connection(SilcServer server,
-                                  char *remote_host, uint32 port)
+static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
+                             SilcPacket packet)
 {
-  SilcServerConnection sconn;
+  SilcIDListData idata = silc_packet_get_context(sock);
+  SilcSKE ske;
+
+  SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s], sock %p",
+                 idata->sconn->remote_host, idata->sconn->remote_port,
+                 SILC_CONNTYPE_STRING(idata->conn_type), sock));
+
+  /* Allocate SKE */
+  ske = silc_ske_alloc(server->rng, server->schedule, NULL,
+                      server->public_key, NULL, sock);
+  if (!ske) {
+    silc_packet_free(packet);
+    return;
+  }
 
-  /* Allocate connection object for hold connection specific stuff. */
-  sconn = silc_calloc(1, sizeof(*sconn));
-  sconn->server = server;
-  sconn->remote_host = strdup(remote_host);
-  sconn->remote_port = port;
+  /* Set SKE callbacks */
+  silc_ske_set_callbacks(ske, NULL, silc_server_rekey_resp_completion, sock);
 
-  silc_task_register(server->timeout_queue, 0, 
-                    silc_server_connect_router,
-                    (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                    SILC_TASK_PRI_NORMAL);
+  /* Perform rekey */
+  idata->sconn->op = silc_ske_rekey_responder(ske, sock, idata->rekey,
+                                             packet);
 }
 
+
+/****************************** Disconnection *******************************/
+
+/* Destroys packet stream. */
+
 SILC_TASK_CALLBACK(silc_server_close_connection_final)
 {
-  silc_socket_free((SilcSocketConnection)context);
+  silc_packet_stream_unref(context);
 }
 
 /* Closes connection to socket connection */
 
 void silc_server_close_connection(SilcServer server,
-                                 SilcSocketConnection sock)
-{
-  SILC_LOG_INFO(("Closing connection %s:%d [%s] (%d)", 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"), sock->sock));
-
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(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);
-
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
-  server->sockets[sock->sock] = NULL;
-
-  silc_task_register(server->timeout_queue, 0, 
-                    silc_server_close_connection_final,
-                    (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
-                    SILC_TASK_PRI_NORMAL);
+                                 SilcPacketStream sock)
+{
+  SilcIDListData idata = silc_packet_get_context(sock);
+  char tmp[128];
+  const char *hostname;
+  SilcUInt16 port;
+
+  if (!silc_packet_stream_is_valid(sock))
+    return;
+
+  memset(tmp, 0, sizeof(tmp));
+  //  silc_socket_get_error(sock, tmp, sizeof(tmp));
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                             NULL, &hostname, NULL, &port);
+  SILC_LOG_INFO(("Closing connection %s:%d [%s] %s", hostname, port,
+                idata ? SILC_CONNTYPE_STRING(idata->conn_type) : "",
+                tmp[0] ? tmp : ""));
+
+  //  silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
+
+  if (idata && idata->sconn) {
+    silc_server_connection_free(idata->sconn);
+    idata->sconn = NULL;
+  }
+
+  /* Take a reference and then destroy the stream.  The last reference
+     is released later in a timeout callback. */
+  silc_packet_stream_ref(sock);
+  silc_packet_stream_destroy(sock);
+
+  /* Close connection with timeout */
+  server->stat.conn_num--;
+  silc_schedule_task_del_by_all(server->schedule, 0,
+                               silc_server_close_connection_final, sock);
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_close_connection_final,
+                                sock, 0, 1);
 }
 
-/* Sends disconnect message to remote connection and disconnects the 
+/* Sends disconnect message to remote connection and disconnects the
    connection. */
 
 void silc_server_disconnect_remote(SilcServer server,
-                                  SilcSocketConnection sock,
-                                  const char *fmt, ...)
+                                  SilcPacketStream sock,
+                                  SilcStatus status, ...)
 {
+  unsigned char buf[512];
   va_list ap;
-  unsigned char buf[4096];
+  char *cp;
 
   if (!sock)
     return;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  SILC_LOG_DEBUG(("Disconnecting remote host"));
+  SILC_LOG_DEBUG(("Disconnecting remote host, sock %p", sock));
 
-  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")));
+  va_start(ap, status);
+  cp = va_arg(ap, char *);
+  if (cp)
+    silc_vsnprintf(buf, sizeof(buf), cp, ap);
+  va_end(ap);
 
-  /* 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,  
-                         buf, strlen(buf), TRUE);
+  /* Send SILC_PACKET_DISCONNECT */
+  silc_packet_send_va(sock, SILC_PACKET_DISCONNECT, 0,
+                     SILC_STR_UI_CHAR(status),
+                     SILC_STR_UI8_STRING(cp ? buf : NULL),
+                     SILC_STR_END);
 
-  /* Mark the connection to be disconnected */
-  SILC_SET_DISCONNECTED(sock);
+  /* Close connection */
   silc_server_close_connection(server, sock);
 }
 
-typedef struct {
-  SilcServer server;
-  SilcClientEntry client;
-} *FreeClientInternal;
-
-SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
-{
-  FreeClientInternal i = (FreeClientInternal)context;
-
-  silc_idlist_del_data(i->client);
-  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
-  silc_free(i);
-}
-
 /* Frees client data and notifies about client's signoff. */
 
-void silc_server_free_client_data(SilcServer server, 
-                                 SilcSocketConnection sock,
-                                 SilcClientEntry client, 
+void silc_server_free_client_data(SilcServer server,
+                                 SilcPacketStream sock,
+                                 SilcClientEntry client,
                                  int notify,
-                                 char *signoff)
+                                 const char *signoff)
 {
-  FreeClientInternal i = silc_calloc(1, sizeof(*i));
-
-  /* If there is pending outgoing data for the client then purge it
-     to the network before removing the client entry. */
-  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
-      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
-    server->stat.packets_sent++;
+  SILC_LOG_DEBUG(("Freeing client %p data", client));
 
-    if (sock->outbuf->data - sock->outbuf->head)
-     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+  if (client->id) {
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_SIGNOFF);
 
-    silc_packet_send(sock, TRUE);
-
-    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-    silc_buffer_clear(sock->outbuf);
+    /* Send SIGNOFF notify to routers. */
+    if (notify)
+      silc_server_send_notify_signoff(server, SILC_PRIMARY_ROUTE(server),
+                                     SILC_BROADCAST(server), client->id,
+                                     signoff);
   }
 
-  /* 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, signoff);
-
   /* Remove client from all channels */
   if (notify)
-    silc_server_remove_from_channels(server, NULL, client, 
-                                    TRUE, signoff, TRUE);
+    silc_server_remove_from_channels(server, NULL, client,
+                                    TRUE, (char *)signoff, TRUE, FALSE);
   else
-    silc_server_remove_from_channels(server, NULL, client, 
-                                    FALSE, NULL, FALSE);
+    silc_server_remove_from_channels(server, NULL, client,
+                                    FALSE, NULL, FALSE, FALSE);
 
-  /* We will not delete the client entry right away. We will take it
-     into history (for WHOWAS command) for 5 minutes */
-  i->server = server;
-  i->client = client;
-  silc_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;
-
-  /* Free the client entry and everything in it */
+  /* Remove this client from watcher list if it is */
+  silc_server_del_from_watcher_list(server, client);
+
+  /* Remove client's public key from repository, this will free it too. */
+  if (client->data.public_key) {
+    silc_skr_del_public_key(server->repository, client->data.public_key,
+                           client);
+    client->data.public_key = NULL;
+  }
+
+  /* Update statistics */
   server->stat.my_clients--;
   server->stat.clients--;
-  if (server->server_type == SILC_ROUTER)
+  if (server->stat.cell_clients)
     server->stat.cell_clients--;
+  SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+  SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+  silc_schedule_task_del_by_context(server->schedule, client);
+
+  if (client->data.sconn) {
+    silc_server_connection_free(client->data.sconn);
+    client->data.sconn = NULL;
+  }
+
+  /* We will not delete the client entry right away. We will take it
+     into history (for WHOWAS command) for 5 minutes, unless we're
+     shutting down server. */
+  if (!server->server_shutdown) {
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    client->mode = 0;
+    client->router = NULL;
+    client->connection = NULL;
+    client->data.created = silc_time();
+    silc_dlist_del(server->expired_clients, client);
+    silc_dlist_add(server->expired_clients, client);
+  } else {
+    /* Delete directly since we're shutting down server */
+    SILC_LOG_DEBUG(("Delete client directly"));
+    silc_idlist_del_data(client);
+    silc_idlist_del_client(server->local_list, client);
+  }
 }
 
 /* Frees user_data pointer from socket connection object. This also sends
    appropriate notify packets to the network to inform about leaving
    entities. */
 
-void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock)
+void silc_server_free_sock_user_data(SilcServer server,
+                                    SilcPacketStream sock,
+                                    const char *signoff_message)
 {
-  SILC_LOG_DEBUG(("Start"));
+  SilcIDListData idata;
+  const char *ip;
+  SilcUInt16 port;
 
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    {
-      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
-      break;
-    }
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    {
-      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+  if (!sock)
+    return;
 
-      /* 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);
+  SILC_LOG_DEBUG(("Start, sock %p", sock));
 
-      /* 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;
-      }
+  idata = silc_packet_get_context(sock);
+  if (!idata)
+    return;
 
-      /* 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_schedule_task_del_by_all(server->schedule, 0, silc_server_do_rekey,
+                               sock);
 
-      silc_idlist_del_data(user_data);
-      silc_free(user_data);
-      break;
+  /* Cancel active protocols */
+  if (idata) {
+    if (idata->sconn && idata->sconn->op) {
+      SILC_LOG_DEBUG(("Abort active protocol"));
+      silc_async_abort(idata->sconn->op, NULL, NULL);
+    }
+    if (idata->conn_type == SILC_CONN_UNKNOWN &&
+        ((SilcUnknownEntry)idata)->op) {
+      SILC_LOG_DEBUG(("Abort active protocol"));
+      silc_async_abort(((SilcUnknownEntry)idata)->op, NULL, NULL);
     }
   }
 
-  sock->user_data = NULL;
-}
+  switch (idata->conn_type) {
+  case SILC_CONN_CLIENT:
+    {
+      SilcClientEntry client_entry = (SilcClientEntry)idata;
+      silc_server_free_client_data(server, sock, client_entry, TRUE,
+                                  signoff_message);
+      silc_packet_set_context(sock, NULL);
+      break;
+    }
 
-/* This function is used to remove all client entries by the server `entry'.
-   This is called when the connection is lost to the server. In this case
-   we must invalidate all the client entries owned by the server `entry'. 
-   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
-   distributed to our local clients. */
+  case SILC_CONN_SERVER:
+  case SILC_CONN_ROUTER:
+    {
+      SilcServerEntry user_data = (SilcServerEntry)idata;
+      SilcServerEntry backup_router = NULL;
 
-int silc_server_remove_clients_by_server(SilcServer server, 
-                                        SilcServerEntry entry,
-                                        int server_signoff)
-{
-  SilcIDCacheList list = NULL;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcClientEntry client = NULL;
-  SilcBuffer idp;
-  SilcClientEntry *clients = NULL;
-  uint32 clients_c = 0;
-  unsigned char **argv = NULL;
-  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
-  int i;
+      SILC_LOG_DEBUG(("Freeing server %p data", user_data));
 
-  SILC_LOG_DEBUG(("Start"));
+      if (user_data->id)
+       backup_router = silc_server_backup_get(server, user_data->id);
 
-  if (server_signoff) {
-    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
-    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
-    argv[argc] = idp->data;
-    argv_lens[argc] = idp->len;
-    argv_types[argc] = argc + 1;
-    argc++;
-    silc_buffer_free(idp);
-  }
+      if (!server->backup_router && server->server_type == SILC_ROUTER &&
+         backup_router == server->id_entry &&
+         idata->conn_type != SILC_CONN_ROUTER)
+       backup_router = NULL;
 
-  if (silc_idcache_find_by_id(server->local_list->clients, 
-                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+      if (server->server_shutdown || server->backup_noswitch)
+       backup_router = NULL;
 
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (client->data.registered == FALSE) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
+      silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                                 NULL, NULL, &ip, &port);
 
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
+      /* If this was our primary router connection then we're lost to
+        the outside world. */
+      if (server->router == user_data) {
+       /* Check whether we have a backup router connection */
+       if (!backup_router || backup_router == user_data) {
+         if (!server->no_reconnect)
+           silc_server_create_connections(server);
+         server->id_entry->router = NULL;
+         server->router = NULL;
+         server->standalone = TRUE;
+         server->backup_primary = FALSE;
+         backup_router = NULL;
+       } else {
+         if (server->id_entry != backup_router) {
+           SILC_LOG_INFO(("New primary router is backup router %s",
+                          backup_router->server_name));
+           server->id_entry->router = backup_router;
+           server->router = backup_router;
+           server->router_connect = time(0);
+           server->backup_primary = TRUE;
+           backup_router->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+           /* Send START_USE to backup router to indicate we have switched */
+           silc_server_backup_send_start_use(server,
+                                             backup_router->connection,
+                                             FALSE);
+         } else {
+           SILC_LOG_INFO(("We are now new primary router in this cell"));
+           server->id_entry->router = NULL;
+           server->router = NULL;
+           server->standalone = TRUE;
          }
 
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
-       }
-
-       /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, FALSE, 
-                                        NULL, FALSE);
-       silc_idlist_del_client(server->local_list, client);
+         /* We stop here to take a breath */
+         sleep(2);
 
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
-      }
-    }
-    silc_idcache_list_free(list);
-  }
-  
-  if (silc_idcache_find_by_id(server->global_list->clients, 
-                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+         if (server->backup_router) {
+           server->server_type = SILC_ROUTER;
 
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (client->data.registered == FALSE) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-       
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
+           /* We'll need to constantly try to reconnect to the primary
+              router so that we'll see when it comes back online. */
+           silc_server_create_connection(server, TRUE, FALSE, ip, port,
+                                        silc_server_backup_connected,
+                                        NULL);
          }
 
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
+         /* Mark this connection as replaced */
+         silc_server_backup_replaced_add(server, user_data->id,
+                                         backup_router);
        }
+      } else if (backup_router) {
+       SILC_LOG_INFO(("Enabling the use of backup router %s",
+                      backup_router->server_name));
+
+       /* Mark this connection as replaced */
+       silc_server_backup_replaced_add(server, user_data->id,
+                                       backup_router);
+      } else if (server->server_type == SILC_SERVER &&
+                idata->conn_type == SILC_CONN_ROUTER) {
+       /* Reconnect to the router (backup) */
+       if (!server->no_reconnect)
+         silc_server_create_connections(server);
+      }
 
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
-       }
+      if (user_data->server_name)
+       SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+                              ("Server %s signoff", user_data->server_name));
+
+      if (!backup_router) {
+       /* Remove all servers that are originated from this server, and
+          remove the clients of those servers too. */
+       silc_server_remove_servers_by_server(server, user_data, TRUE);
+
+#if 0
+       /* Remove the clients that this server owns as they will become
+          invalid now too.  For backup router the server is actually
+          coming from the primary router, so mark that as the owner
+          of this entry. */
+       if (server->server_type == SILC_BACKUP_ROUTER &&
+           sock->type == SILC_CONN_SERVER)
+         silc_server_remove_clients_by_server(server, server->router,
+                                              user_data, TRUE);
+       else
+#endif
+         silc_server_remove_clients_by_server(server, user_data,
+                                              user_data, TRUE);
 
-       /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, FALSE,
-                                        NULL, FALSE);
-       silc_idlist_del_client(server->global_list, client);
+       /* Remove channels owned by this server */
+       if (server->server_type == SILC_SERVER)
+         silc_server_remove_channels_by_server(server, user_data);
+      } else {
+       /* Enable local server connections that may be disabled */
+       silc_server_local_servers_toggle_enabled(server, TRUE);
+
+       /* Update the client entries of this server to the new backup
+          router.  If we are the backup router we also resolve the real
+          servers for the clients.  After updating is over this also
+          removes the clients that this server explicitly owns. */
+       silc_server_update_clients_by_server(server, user_data,
+                                            backup_router, TRUE);
+
+       /* If we are router and just lost our primary router (now standlaone)
+          we remove everything that was behind it, since we don't know
+          any better. */
+       if (server->server_type == SILC_ROUTER && server->standalone)
+         /* Remove all servers that are originated from this server, and
+            remove the clients of those servers too. */
+         silc_server_remove_servers_by_server(server, user_data, TRUE);
+
+       /* Finally remove the clients that are explicitly owned by this
+          server.  They go down with the server. */
+       silc_server_remove_clients_by_server(server, user_data,
+                                            user_data, TRUE);
+
+       /* Update our server cache to use the new backup router too. */
+       silc_server_update_servers_by_server(server, user_data, backup_router);
+       if (server->server_type == SILC_SERVER)
+         silc_server_update_channels_by_server(server, user_data,
+                                               backup_router);
+
+       /* Send notify about primary router going down to local operators */
+       if (server->backup_router)
+         SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+                                SILC_NOTIFY_TYPE_NONE,
+                                ("%s switched to backup router %s "
+                                 "(we are primary router now)",
+                                 server->server_name, server->server_name));
+       else if (server->router)
+         SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+                                SILC_NOTIFY_TYPE_NONE,
+                                ("%s switched to backup router %s",
+                                 server->server_name,
+                                 server->router->server_name));
+      }
+      server->backup_noswitch = FALSE;
 
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
+      if (idata->sconn) {
+       silc_server_connection_free(idata->sconn);
+       idata->sconn = NULL;
       }
-    }
-    silc_idcache_list_free(list);
-  }
 
-  /* Send the SERVER_SIGNOFF notify */
-  if (server_signoff) {
-    SilcBuffer args;
+      /* Statistics */
+      if (idata->conn_type == SILC_CONN_SERVER) {
+       server->stat.my_servers--;
+       server->stat.servers--;
+       SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+      } else if (idata->conn_type == SILC_CONN_ROUTER) {
+       server->stat.my_routers--;
+       server->stat.routers--;
+       SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+      }
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_servers--;
 
-    /* Send SERVER_SIGNOFF notify to our primary router */
-    if (!server->standalone && server->router) {
-      args = silc_argument_payload_encode(1, argv, argv_lens,
-                                         argv_types);
-      silc_server_send_notify_args(server, 
-                                  server->router->connection,
-                                  server->server_type == SILC_SERVER ? 
-                                  FALSE : TRUE, 
-                                  SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
-      silc_buffer_free(args);
-    }
+      /* Free the server entry */
+      silc_server_backup_del(server, user_data);
+      silc_server_backup_replaced_del(server, user_data);
+      silc_idlist_del_data(user_data);
+      if (!silc_idlist_del_server(server->local_list, user_data))
+       silc_idlist_del_server(server->global_list, user_data);
+
+      if (backup_router && backup_router != server->id_entry) {
+       /* Announce all of our stuff that was created about 5 minutes ago.
+          The backup router knows all the other stuff already. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server, FALSE, time(0) - 300,
+                                      backup_router->connection);
+
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, time(0) - 300,
+                                    backup_router->connection);
+       silc_server_announce_channels(server, time(0) - 300,
+                                     backup_router->connection);
+      }
 
-    args = silc_argument_payload_encode(argc, argv, argv_lens,
-                                       argv_types);
-    /* Send to local clients */
-    for (i = 0; i < clients_c; i++) {
-      silc_server_send_notify_args(server, clients[i]->connection,
-                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
+      silc_packet_set_context(sock, NULL);
+      break;
     }
 
-    silc_free(clients);
-    silc_buffer_free(args);
-    silc_free(argv);
-    silc_free(argv_lens);
-    silc_free(argv_types);
-  }
-
-  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;
-
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (chl->client->router)
-      return TRUE;
-  }
-
-  return FALSE;
-}
+  default:
+    {
+      SilcUnknownEntry entry = (SilcUnknownEntry)idata;
 
-/* Checks whether given channel has locally connected users.  If it does this
-   returns TRUE and FALSE if there is not one locally connected client. */
+      SILC_LOG_DEBUG(("Freeing unknown connection data %p", entry));
 
-int silc_server_channel_has_local(SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
+      if (idata->sconn) {
+       if (server->router_conn == idata->sconn) {
+         if (!server->no_reconnect)
+           silc_server_create_connections(server);
+         server->router_conn = NULL;
+       }
 
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (!chl->client->router)
-      return TRUE;
+       silc_server_connection_free(idata->sconn);
+       idata->sconn = NULL;
+      }
+      silc_idlist_del_data(idata);
+      silc_free(entry);
+      silc_packet_set_context(sock, NULL);
+      break;
+    }
   }
-
-  return FALSE;
 }
 
 /* Removes client from all channels it has joined. This is used when client
    connection is disconnected. If the client on a channel is last, the
    channel is removed as well. This sends the SIGNOFF notify types. */
 
-void silc_server_remove_from_channels(SilcServer server, 
-                                     SilcSocketConnection sock,
+void silc_server_remove_from_channels(SilcServer server,
+                                     SilcPacketStream sock,
                                      SilcClientEntry client,
-                                     int notify,
-                                     char *signoff_message,
-                                     int keygen)
+                                     SilcBool notify,
+                                     const char *signoff_message,
+                                     SilcBool keygen,
+                                     SilcBool killed)
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
-  SilcBuffer clidp;
-
-  SILC_LOG_DEBUG(("Start"));
+  SilcHashTableList htl;
+  SilcBuffer clidp = NULL;
 
-  if (!client || !client->id)
+  if (!client)
     return;
 
-  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  if (notify && !client->id)
+    notify = FALSE;
+
+  SILC_LOG_DEBUG(("Removing client %s from joined channels",
+                 notify ? silc_id_render(client->id, SILC_ID_CLIENT) : ""));
+
+  if (notify) {
+    clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    if (!clidp)
+      notify = FALSE;
+  }
 
   /* 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);
-
-    /* Remove channel if there is no users anymore */
-    if (server->server_type == SILC_ROUTER &&
-       silc_list_count(channel->user_list) < 2) {
-      server->stat.my_channels--;
-
-      if (channel->rekey)
-       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
-
-      if (channel->founder_key) {
-       /* The founder auth data exists, do not remove the channel entry */
-       SilcChannelClientEntry chl2;
-
-       channel->id = NULL;
-
-       silc_list_start(channel->user_list);
-       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-         silc_list_del(chl2->client->channels, chl2);
-         silc_list_del(channel->user_list, chl2);
-         silc_free(chl2);
-       }
-       continue;
-      }
-
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
+    /* Remove channel if this is last client leaving the channel, unless
+       the channel is permanent. */
+    if (server->server_type != SILC_SERVER &&
+       silc_hash_table_count(channel->user_list) < 2) {
+      silc_server_channel_delete(server, channel);
       continue;
     }
 
-    /* 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(client->channels, channel);
+    silc_hash_table_del(channel->user_list, client);
+    channel->user_count--;
 
     /* If there is no global users on the channel anymore mark the channel
-       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_SERVER && channel->global_users &&
+       chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
+    memset(chl, 'A', sizeof(*chl));
+    silc_free(chl);
+
+    /* Update statistics */
+    if (SILC_IS_LOCAL(client))
+      server->stat.my_chanclients--;
+    if (server->server_type == SILC_ROUTER) {
+      server->stat.cell_chanclients--;
+      server->stat.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. */
+       need the channel entry anymore, we can remove it safely, unless the
+       channel is permanent channel */
     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_SIGNOFF, 
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                          SILC_NOTIFY_TYPE_SIGNOFF,
                                           signoff_message ? 2 : 1,
-                                          clidp->data, clidp->len,
+                                          clidp->data, silc_buffer_len(clidp),
                                           signoff_message, signoff_message ?
                                           strlen(signoff_message) : 0);
 
-      if (channel->rekey)
-       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
-
-      if (channel->founder_key) {
-       /* The founder auth data exists, do not remove the channel entry */
-       SilcChannelClientEntry chl2;
-
-       channel->id = NULL;
-
-       silc_list_start(channel->user_list);
-       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-         silc_list_del(chl2->client->channels, chl2);
-         silc_list_del(channel->user_list, chl2);
-         silc_free(chl2);
-       }
-       continue;
-      }
-
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
+      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+      silc_server_channel_delete(server, channel);
       continue;
     }
 
-    /* Send notify to channel about client leaving SILC and thus
-       the entire channel. */
+    /* Send notify to channel about client leaving SILC and channel too */
     if (notify)
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                        SILC_NOTIFY_TYPE_SIGNOFF, 
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                        SILC_NOTIFY_TYPE_SIGNOFF,
                                         signoff_message ? 2 : 1,
-                                        clidp->data, clidp->len,
+                                        clidp->data, silc_buffer_len(clidp),
                                         signoff_message, signoff_message ?
                                         strlen(signoff_message) : 0);
 
+    if (killed && clidp) {
+      /* Remove the client from channel's invite list */
+      if (channel->invite_list &&
+         silc_hash_table_count(channel->invite_list)) {
+       SilcBuffer ab;
+       SilcArgumentPayload iargs;
+       ab = silc_argument_payload_encode_one(NULL, clidp->data,
+                                             silc_buffer_len(clidp), 3);
+       iargs = silc_argument_payload_parse(ab->data, silc_buffer_len(ab), 1);
+       silc_server_inviteban_process(server, channel->invite_list, 1, iargs);
+       silc_buffer_free(ab);
+       silc_argument_payload_free(iargs);
+      }
+    }
+
+    /* Don't create keys if we are shutting down */
+    if (server->server_shutdown)
+      continue;
+
+    /* Re-generate channel key if needed */
     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))
+       continue;
+
       /* Send the channel key to the channel. The key of course is not sent
         to the client who was removed from the channel. */
-      silc_server_send_channel_key(server, client->connection, channel, 
-                                  server->server_type == SILC_ROUTER ? 
+      silc_server_send_channel_key(server, client->connection, channel,
+                                  server->server_type == SILC_ROUTER ?
                                   FALSE : !server->standalone);
     }
   }
 
-  silc_buffer_free(clidp);
+  silc_hash_table_list_reset(&htl);
+  if (clidp)
+    silc_buffer_free(clidp);
 }
 
 /* Removes client from one channel. This is used for example when client
@@ -2611,142 +3550,82 @@ void silc_server_remove_from_channels(SilcServer server,
    last client leaves the channel. If `notify' is FALSE notify messages
    are not sent. */
 
-int silc_server_remove_from_one_channel(SilcServer server, 
-                                       SilcSocketConnection sock,
-                                       SilcChannelEntry channel,
-                                       SilcClientEntry client,
-                                       int notify)
+SilcBool silc_server_remove_from_one_channel(SilcServer server,
+                                        SilcPacketStream sock,
+                                        SilcChannelEntry channel,
+                                        SilcClientEntry client,
+                                        SilcBool notify)
 {
-  SilcChannelEntry ch;
   SilcChannelClientEntry chl;
   SilcBuffer clidp;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Removing %s from channel %s",
+                 silc_id_render(client->id, SILC_ID_CLIENT),
+                 channel->channel_name));
 
-  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;
+  /* Remove channel if this is last client leaving the channel, unless
+     the channel is permanent. */
+  if (server->server_type != SILC_SERVER &&
+      silc_hash_table_count(channel->user_list) < 2) {
+    silc_server_channel_delete(server, channel);
+    return FALSE;
+  }
 
-    ch = chl->channel;
+  silc_hash_table_del(client->channels, channel);
+  silc_hash_table_del(channel->user_list, client);
+  channel->user_count--;
 
-    /* Remove channel from client's channel list */
-    silc_list_del(client->channels, chl);
+  /* If there is no global users on the channel anymore mark the channel
+     as local channel. Do not check if the client is local client. */
+  if (server->server_type == SILC_SERVER && channel->global_users &&
+      chl->client->router && !silc_server_channel_has_global(channel))
+    channel->global_users = FALSE;
 
-    /* Remove channel if there is no users anymore */
-    if (server->server_type == SILC_ROUTER &&
-       silc_list_count(channel->user_list) < 2) {
-      if (channel->rekey)
-       silc_task_unregister_by_context(server->timeout_queue, 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;
-    }
+  memset(chl, 'O', sizeof(*chl));
+  silc_free(chl);
 
-    /* Remove client from channel's client list */
-    silc_list_del(channel->user_list, chl);
-    silc_free(chl);
+  /* Update statistics */
+  if (SILC_IS_LOCAL(client))
     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);
-
-      silc_buffer_free(clidp);
-
-      if (channel->rekey)
-       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
-
-      if (channel->founder_key) {
-       /* The founder auth data exists, do not remove the channel entry */
-       SilcChannelClientEntry chl2;
-
-       channel->id = NULL;
-
-       silc_list_start(channel->user_list);
-       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-         silc_list_del(chl2->client->channels, chl2);
-         silc_list_del(channel->user_list, chl2);
-         silc_free(chl2);
-       }
-       return FALSE;
-      }
-
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
-      return FALSE;
-    }
-
-    /* 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);
-    break;
+  if (server->server_type == SILC_ROUTER) {
+    server->stat.cell_chanclients--;
+    server->stat.chanclients--;
   }
 
-  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'. */
-
-int silc_server_client_on_channel(SilcClientEntry client,
-                                 SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  if (!clidp)
+    notify = 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, unless the
+     channel is permanent channel */
+  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, TRUE,
+                                        SILC_NOTIFY_TYPE_LEAVE, 1,
+                                        clidp->data, silc_buffer_len(clidp));
 
-  if (!client || !channel)
+    silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+    silc_server_channel_delete(server, channel);
+    silc_buffer_free(clidp);
     return FALSE;
+  }
 
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END)
-    if (chl->channel == channel)
-      return TRUE;
-
-  return FALSE;
-}
-
-/* Timeout callback. This is called if connection is idle or for some
-   other reason is not responding within some period of time. This 
-   disconnects the remote end. */
-
-SILC_TASK_CALLBACK(silc_server_timeout_remote)
-{
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
-
-  if (!sock)
-    return;
-
-  if (sock->user_data)
-    silc_server_free_sock_user_data(server, sock);
+  /* Send notify to channel about client leaving the channel */
+  if (notify)
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                      SILC_NOTIFY_TYPE_LEAVE, 1,
+                                      clidp->data, silc_buffer_len(clidp));
 
-  silc_server_disconnect_remote(server, sock, 
-                               "Server closed connection: "
-                               "Connection timeout");
+  silc_buffer_free(clidp);
+  return TRUE;
 }
 
 /* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
@@ -2754,44 +3633,62 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
    are created by routers thus this function is never used by normal
    server. */
 
-SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
+SilcChannelEntry silc_server_create_new_channel(SilcServer server,
                                                SilcServerID *router_id,
-                                               char *cipher, 
+                                               char *cipher,
                                                char *hmac,
                                                char *channel_name,
                                                int broadcast)
 {
   SilcChannelID *channel_id;
   SilcChannelEntry entry;
-  SilcCipher key;
+  SilcCipher send_key, receive_key;
   SilcHmac newhmac;
 
-  SILC_LOG_DEBUG(("Creating new channel"));
+  SILC_LOG_DEBUG(("Creating new channel %s", channel_name));
 
   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))
+  if (!silc_cipher_alloc(cipher, &send_key))
+    return NULL;
+  if (!silc_cipher_alloc(cipher, &receive_key)) {
+    silc_cipher_free(send_key);
     return NULL;
+  }
 
   /* Allocate hmac */
   if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
-    silc_cipher_free(key);
+    silc_cipher_free(send_key);
+    silc_cipher_free(receive_key);
+    return NULL;
+  }
+
+  channel_name = strdup(channel_name);
+
+  /* Create the channel ID */
+  if (!silc_id_create_channel_id(server, router_id, server->rng,
+                                &channel_id)) {
+    silc_free(channel_name);
+    silc_cipher_free(send_key);
+    silc_cipher_free(receive_key);
+    silc_hmac_free(newhmac);
     return NULL;
   }
 
-  channel_name = strdup(channel_name);
-
   /* Create the channel */
-  silc_id_create_channel_id(router_id, server->rng, &channel_id);
-  entry = silc_idlist_add_channel(server->local_list, channel_name, 
-                                 SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key, newhmac);
+  entry = silc_idlist_add_channel(server->local_list, channel_name,
+                                 SILC_CHANNEL_MODE_NONE, channel_id,
+                                 NULL, send_key, receive_key, newhmac);
   if (!entry) {
     silc_free(channel_name);
+    silc_cipher_free(send_key);
+    silc_cipher_free(receive_key);
+    silc_hmac_free(newhmac);
+    silc_free(channel_id);
     return NULL;
   }
 
@@ -2799,77 +3696,133 @@ 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(send_key) / 8)) {
+    silc_idlist_del_channel(server->local_list, entry);
+    return NULL;
+  }
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE)
-    silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, 
+  if (broadcast)
+    silc_server_send_new_channel(server, SILC_PRIMARY_ROUTE(server), TRUE,
+                                channel_name, entry->id,
                                 silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
+  /* Distribute to backup routers */
+  if (broadcast && server->server_type == SILC_ROUTER) {
+    SilcBuffer packet;
+    unsigned char cid[32];
+    SilcUInt32 name_len = strlen(channel_name);
+    SilcUInt32 id_len;
+
+    silc_id_id2str(entry->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
+    packet = silc_channel_payload_encode(channel_name, name_len,
+                                        cid, id_len, entry->mode);
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+                           packet->data, silc_buffer_len(packet), FALSE,
+                           TRUE);
+    silc_buffer_free(packet);
+  }
+
   server->stat.my_channels++;
+  if (server->server_type == SILC_ROUTER) {
+    server->stat.channels++;
+    server->stat.cell_channels++;
+    entry->users_resolved = TRUE;
+  }
 
   return entry;
 }
 
 /* Same as above but creates the channel with Channel ID `channel_id. */
 
-SilcChannelEntry 
-silc_server_create_new_channel_with_id(SilcServer server, 
-                                      char *cipher, 
+SilcChannelEntry
+silc_server_create_new_channel_with_id(SilcServer server,
+                                      char *cipher,
                                       char *hmac,
                                       char *channel_name,
                                       SilcChannelID *channel_id,
                                       int broadcast)
 {
   SilcChannelEntry entry;
-  SilcCipher key;
+  SilcCipher send_key, receive_key;
   SilcHmac newhmac;
 
-  SILC_LOG_DEBUG(("Creating new channel"));
+  SILC_LOG_DEBUG(("Creating new channel %s", channel_name));
 
   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))
+  if (!silc_cipher_alloc(cipher, &send_key))
+    return NULL;
+  if (!silc_cipher_alloc(cipher, &receive_key)) {
+    silc_cipher_free(send_key);
     return NULL;
+  }
 
   /* Allocate hmac */
   if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
-    silc_cipher_free(key);
+    silc_cipher_free(send_key);
+    silc_cipher_free(receive_key);
     return NULL;
   }
 
   channel_name = strdup(channel_name);
 
   /* Create the channel */
-  entry = silc_idlist_add_channel(server->local_list, channel_name, 
-                                 SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key, newhmac);
+  entry = silc_idlist_add_channel(server->local_list, channel_name,
+                                 SILC_CHANNEL_MODE_NONE, channel_id,
+                                 NULL, send_key, receive_key, newhmac);
   if (!entry) {
+    silc_cipher_free(send_key);
+    silc_cipher_free(receive_key);
+    silc_hmac_free(newhmac);
     silc_free(channel_name);
     return NULL;
   }
 
   /* 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(send_key) / 8)) {
+    silc_idlist_del_channel(server->local_list, entry);
+    return NULL;
+  }
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE)
-    silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, 
+  if (broadcast)
+    silc_server_send_new_channel(server, SILC_PRIMARY_ROUTE(server), TRUE,
+                                channel_name, entry->id,
                                 silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
+  /* Distribute to backup routers */
+  if (broadcast && server->server_type == SILC_ROUTER) {
+    SilcBuffer packet;
+    unsigned char cid[32];
+    SilcUInt32 name_len = strlen(channel_name);
+    SilcUInt32 id_len;
+
+    silc_id_id2str(entry->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
+    packet = silc_channel_payload_encode(channel_name, name_len,
+                                        cid, id_len, entry->mode);
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+                           packet->data, silc_buffer_len(packet), FALSE,
+                           TRUE);
+    silc_buffer_free(packet);
+  }
+
   server->stat.my_channels++;
+  if (server->server_type == SILC_ROUTER) {
+    server->stat.channels++;
+    server->stat.cell_channels++;
+    entry->users_resolved = TRUE;
+  }
 
   return entry;
 }
@@ -2878,54 +3831,65 @@ silc_server_create_new_channel_with_id(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
 {
+  SilcServer server = app_context;
   SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
-  SilcServer server = (SilcServer)rekey->context;
 
-  silc_server_create_channel_key(server, rekey->channel, rekey->key_len);
-  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
+  rekey->task = NULL;
+
+  /* Return now if we are shutting down */
+  if (server->server_shutdown)
+    return;
 
-  silc_task_register(server->timeout_queue, 0, 
-                    silc_server_channel_key_rekey,
-                    (void *)rekey, 3600, 0,
-                    SILC_TASK_TIMEOUT,
-                    SILC_TASK_PRI_NORMAL);
+  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, 
-                                   SilcChannelEntry channel,
-                                   uint32 key_len)
+SilcBool silc_server_create_channel_key(SilcServer server,
+                                       SilcChannelEntry channel,
+                                       SilcUInt32 key_len)
 {
   int i;
-  unsigned char channel_key[32], hash[32];
-  uint32 len;
-
-  SILC_LOG_DEBUG(("Generating channel key"));
+  unsigned char channel_key[32], hash[SILC_HASH_MAXLEN];
+  SilcUInt32 len;
 
   if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
     SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
-    return;
+    return TRUE;
   }
 
-  if (!channel->channel_key)
-    if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
-      return;
+  SILC_LOG_DEBUG(("Generating channel %s key", channel->channel_name));
+
+  if (!channel->send_key)
+    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->send_key)) {
+      channel->send_key = NULL;
+      return FALSE;
+    }
+  if (!channel->receive_key)
+    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->receive_key)) {
+      silc_cipher_free(channel->send_key);
+      channel->send_key = channel->receive_key = NULL;
+      return FALSE;
+    }
 
   if (key_len)
     len = key_len;
   else if (channel->key_len)
     len = channel->key_len / 8;
   else
-    len = silc_cipher_get_key_len(channel->channel_key) / 8;
+    len = silc_cipher_get_key_len(channel->send_key) / 8;
 
   /* Create channel key */
   for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
-  
+
   /* Set the key */
-  silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
+  silc_cipher_set_key(channel->send_key, channel_key, len * 8, TRUE);
+  silc_cipher_set_key(channel->receive_key, channel_key, len * 8, FALSE);
 
   /* Remove old key if exists */
   if (channel->key) {
@@ -2935,35 +3899,43 @@ void silc_server_create_channel_key(SilcServer server,
 
   /* Save the key */
   channel->key_len = len * 8;
-  channel->key = silc_calloc(len, sizeof(*channel->key));
-  memcpy(channel->key, channel_key, len);
+  channel->key = silc_memdup(channel_key, len);
   memset(channel_key, 0, sizeof(channel_key));
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
-  silc_hash_make(channel->hmac->hash, channel->key, len, hash);
-  silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+    if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      silc_cipher_free(channel->send_key);
+      silc_cipher_free(channel->receive_key);
+      channel->send_key = channel->receive_key = NULL;
+      return FALSE;
+    }
+  silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash);
+  silc_hmac_set_key(channel->hmac, hash,
+                   silc_hash_len(silc_hmac_get_hash(channel->hmac)));
   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;
-
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_channel_key_rekey);
-    silc_task_register(server->timeout_queue, 0, 
-                      silc_server_channel_key_rekey,
-                      (void *)channel->rekey, 3600, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+    if (channel->rekey->task)
+      silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+    channel->rekey->task =
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_channel_key_rekey,
+                                    (void *)channel->rekey,
+                                    server->config->channel_rekey_secs, 0);
   }
+
+  return TRUE;
 }
 
-/* Saves the channel key found in the encoded `key_payload' buffer. This 
+/* Saves the channel key found in the encoded `key_payload' buffer. This
    function is used when we receive Channel Key Payload and also when we're
    processing JOIN command reply. Returns entry to the channel. */
 
@@ -2972,17 +3944,16 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
                                              SilcChannelEntry channel)
 {
   SilcChannelKeyPayload payload = NULL;
-  SilcChannelID *id = NULL;
-  unsigned char *tmp, hash[32];
-  uint32 tmp_len;
+  SilcChannelID id;
+  unsigned char *tmp, hash[SILC_HASH_MAXLEN];
+  SilcUInt32 tmp_len;
   char *cipher;
 
-  SILC_LOG_DEBUG(("Start"));
-
   /* Decode channel key payload */
-  payload = silc_channel_key_payload_parse(key_payload);
+  payload = silc_channel_key_payload_parse(key_payload->data,
+                                          silc_buffer_len(key_payload));
   if (!payload) {
-    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    SILC_LOG_ERROR(("Bad channel key payload received, dropped"));
     channel = NULL;
     goto out;
   }
@@ -2992,22 +3963,25 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
     /* Get channel ID */
     tmp = silc_channel_key_get_id(payload, &tmp_len);
-    id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
-    if (!id) {
+    if (!silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
       channel = NULL;
       goto out;
     }
 
-    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+    channel = silc_idlist_find_channel_by_id(server->local_list, &id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+      channel = silc_idlist_find_channel_by_id(server->global_list, &id, NULL);
       if (!channel) {
-       SILC_LOG_ERROR(("Received key for non-existent channel"));
+       if (server->server_type == SILC_ROUTER)
+         SILC_LOG_ERROR(("Received key for non-existent channel %s",
+                         silc_id_render(&id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
   }
 
+  SILC_LOG_DEBUG(("Saving new channel %s key", channel->channel_name));
+
   tmp = silc_channel_key_get_key(payload, &tmp_len);
   if (!tmp) {
     channel = NULL;
@@ -3024,11 +3998,19 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   if (channel->key) {
     memset(channel->key, 0, channel->key_len / 8);
     silc_free(channel->key);
-    silc_cipher_free(channel->channel_key);
+    silc_cipher_free(channel->send_key);
+    silc_cipher_free(channel->receive_key);
   }
 
   /* Create new cipher */
-  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+  if (!silc_cipher_alloc(cipher, &channel->send_key)) {
+    channel->send_key = NULL;
+    channel = NULL;
+    goto out;
+  }
+  if (!silc_cipher_alloc(cipher, &channel->receive_key)) {
+    silc_cipher_free(channel->send_key);
+    channel->send_key = channel->receive_key = NULL;
     channel = NULL;
     goto out;
   }
@@ -3039,15 +4021,23 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   /* Save the key */
   channel->key_len = tmp_len * 8;
-  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
-  memcpy(channel->key, tmp, tmp_len);
-  silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
+  channel->key = silc_memdup(tmp, tmp_len);
+  silc_cipher_set_key(channel->send_key, tmp, channel->key_len, TRUE);
+  silc_cipher_set_key(channel->receive_key, tmp, channel->key_len, FALSE);
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
-  silc_hash_make(channel->hmac->hash, tmp, tmp_len, hash);
-  silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+    if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      silc_cipher_free(channel->send_key);
+      silc_cipher_free(channel->receive_key);
+      channel->send_key = channel->receive_key = NULL;
+      return FALSE;
+    }
+  silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
+  silc_hmac_set_key(channel->hmac, hash,
+                   silc_hash_len(silc_hmac_get_hash(channel->hmac)));
 
   memset(hash, 0, sizeof(hash));
   memset(tmp, 0, tmp_len);
@@ -3055,105 +4045,115 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   if (server->server_type == SILC_ROUTER) {
     if (!channel->rekey)
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
-    channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
-
-    silc_task_unregister_by_callback(server->timeout_queue,
-                                    silc_server_channel_key_rekey);
-    silc_task_register(server->timeout_queue, 0, 
-                      silc_server_channel_key_rekey,
-                      (void *)channel->rekey, 3600, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
+    if (channel->rekey->task)
+      silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+    channel->rekey->task =
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_channel_key_rekey,
+                                    (void *)channel->rekey,
+                                    server->config->channel_rekey_secs, 0);
   }
 
  out:
-  if (id)
-    silc_free(id);
   if (payload)
     silc_channel_key_payload_free(payload);
 
   return channel;
 }
 
-/* Heartbeat callback. This function is set as argument for the
-   silc_socket_set_heartbeat function. The library will call this function
-   at the set time interval. */
-
-void silc_server_perform_heartbeat(SilcSocketConnection sock,
-                                  void *hb_context)
-{
-  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
-
-  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
-                 sock->ip));
-
-  /* Send the heartbeat */
-  silc_server_send_heartbeat(hb->server, sock);
-}
-
 /* Returns assembled of all servers in the given ID list. The packet's
    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;
+  SilcList list;
   SilcIDCacheEntry id_cache;
   SilcServerEntry entry;
   SilcBuffer idp;
+  void *tmp;
 
   /* 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_list_first(list, &id_cache)) {
-      while (id_cache) {
-       entry = (SilcServerEntry)id_cache->context;
-
-       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-
-       *servers = silc_buffer_realloc(*servers, 
-                                      (*servers ? 
-                                       (*servers)->truelen + idp->len : 
-                                       idp->len));
-       silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
-       silc_buffer_put(*servers, idp->data, idp->len);
-       silc_buffer_pull(*servers, idp->len);
-       silc_buffer_free(idp);
-
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
-      }
-    }
+  if (silc_idcache_get_all(id_list->servers, &list)) {
+    silc_list_start(list);
+    while ((id_cache = silc_list_get(list))) {
+      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))
+       continue;
+
+      idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
 
-    silc_idcache_list_free(list);
+      tmp = silc_buffer_realloc(*servers,
+                               (*servers ?
+                                silc_buffer_truelen((*servers)) +
+                                silc_buffer_len(idp) :
+                                silc_buffer_len(idp)));
+      if (!tmp)
+       return;
+      *servers = tmp;
+      silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
+      silc_buffer_put(*servers, idp->data, silc_buffer_len(idp));
+      silc_buffer_pull(*servers, silc_buffer_len(idp));
+      silc_buffer_free(idp);
+    }
   }
 }
 
+static SilcBuffer
+silc_server_announce_encode_notify(SilcNotifyType notify, SilcUInt32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer p;
+
+  va_start(ap, argc);
+  p = silc_notify_payload_encode(notify, argc, ap);
+  va_end(ap);
+
+  return p;
+}
+
 /* This function is used by router to announce existing servers to our
-   primary router when we've connected to it. */
+   primary router when we've connected to it. If `creation_time' is non-zero
+   then only the servers that has been created after the `creation_time'
+   will be announced. */
 
-void silc_server_announce_servers(SilcServer server)
+void silc_server_announce_servers(SilcServer server, SilcBool global,
+                                 unsigned long creation_time,
+                                 SilcPacketStream remote)
 {
   SilcBuffer servers = NULL;
 
   SILC_LOG_DEBUG(("Announcing servers"));
 
   /* Get servers in local list */
-  silc_server_announce_get_servers(server, server->local_list, &servers);
+  silc_server_announce_get_servers(server, silc_packet_get_context(remote),
+                                  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, silc_packet_get_context(remote),
+                                    server->global_list, &servers,
+                                    creation_time);
 
   if (servers) {
     silc_buffer_push(servers, servers->data - servers->head);
-    SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
+    SILC_LOG_HEXDUMP(("servers"), servers->data, silc_buffer_len(servers));
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
-                           servers->data, servers->len, TRUE);
+                           servers->data, silc_buffer_len(servers));
 
     silc_buffer_free(servers);
   }
@@ -3164,140 +4164,336 @@ void silc_server_announce_servers(SilcServer server)
 
 static void silc_server_announce_get_clients(SilcServer server,
                                             SilcIDList id_list,
-                                            SilcBuffer *clients)
+                                            SilcBuffer *clients,
+                                            SilcBuffer *umodes,
+                                            unsigned long creation_time)
 {
-  SilcIDCacheList list;
+  SilcList list;
   SilcIDCacheEntry id_cache;
   SilcClientEntry client;
   SilcBuffer idp;
+  SilcBuffer tmp;
+  unsigned char mode[4];
+  void *tmp2;
 
   /* 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_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-
-       idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
-       *clients = silc_buffer_realloc(*clients, 
-                                      (*clients ? 
-                                       (*clients)->truelen + idp->len : 
-                                       idp->len));
-       silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
-       silc_buffer_put(*clients, idp->data, idp->len);
-       silc_buffer_pull(*clients, idp->len);
-       silc_buffer_free(idp);
-
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
-      }
-    }
+  if (silc_idcache_get_all(id_list->clients, &list)) {
+    silc_list_start(list);
+    while ((id_cache = silc_list_get(list))) {
+      client = (SilcClientEntry)id_cache->context;
+
+      if (creation_time && client->data.created < creation_time)
+       continue;
+      if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
+       continue;
+      if (!client->connection && !client->router)
+       continue;
+
+      SILC_LOG_DEBUG(("Announce Client ID %s",
+                     silc_id_render(client->id, SILC_ID_CLIENT)));
 
-    silc_idcache_list_free(list);
+      idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+      if (!idp)
+       return;
+
+      tmp2 = silc_buffer_realloc(*clients,
+                               (*clients ?
+                                silc_buffer_truelen((*clients)) +
+                                silc_buffer_len(idp) :
+                                silc_buffer_len(idp)));
+      if (!tmp2)
+       return;
+      *clients = tmp2;
+      silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
+      silc_buffer_put(*clients, idp->data, silc_buffer_len(idp));
+      silc_buffer_pull(*clients, silc_buffer_len(idp));
+
+      SILC_PUT32_MSB(client->mode, mode);
+      tmp =
+       silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+                                          2, idp->data, silc_buffer_len(idp),
+                                          mode, 4);
+      tmp2 = silc_buffer_realloc(*umodes,
+                                (*umodes ?
+                                 silc_buffer_truelen((*umodes)) +
+                                 silc_buffer_len(tmp) :
+                                 silc_buffer_len(tmp)));
+      if (!tmp2)
+       return;
+      *umodes = tmp2;
+      silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+      silc_buffer_put(*umodes, tmp->data, silc_buffer_len(tmp));
+      silc_buffer_pull(*umodes, silc_buffer_len(tmp));
+      silc_buffer_free(tmp);
+
+      silc_buffer_free(idp);
+    }
   }
 }
 
 /* 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,
+                                 SilcPacketStream remote)
 {
   SilcBuffer clients = NULL;
+  SilcBuffer umodes = NULL;
 
   SILC_LOG_DEBUG(("Announcing clients"));
 
   /* Get clients in local list */
   silc_server_announce_get_clients(server, server->local_list,
-                                  &clients);
+                                  &clients, &umodes, creation_time);
 
   /* As router we announce our global list as well */
   if (server->server_type == SILC_ROUTER)
     silc_server_announce_get_clients(server, server->global_list,
-                                    &clients);
+                                    &clients, &umodes, creation_time);
 
   if (clients) {
     silc_buffer_push(clients, clients->data - clients->head);
-    SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
+    SILC_LOG_HEXDUMP(("clients"), clients->data, silc_buffer_len(clients));
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
-                           clients->data, clients->len, TRUE);
+                           clients->data, silc_buffer_len(clients));
 
     silc_buffer_free(clients);
   }
+
+  if (umodes) {
+    silc_buffer_push(umodes, umodes->data - umodes->head);
+    SILC_LOG_HEXDUMP(("umodes"), umodes->data, silc_buffer_len(umodes));
+
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           umodes->data, silc_buffer_len(umodes));
+
+    silc_buffer_free(umodes);
+  }
 }
 
-static SilcBuffer 
-silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+/* Returns channel's topic for announcing it */
+
+void silc_server_announce_get_channel_topic(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *topic)
 {
-  va_list ap;
+  SilcBuffer chidp;
+
+  if (channel->topic) {
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+    *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+                                               chidp->data,
+                                               silc_buffer_len(chidp),
+                                               channel->topic,
+                                               strlen(channel->topic));
+    silc_buffer_free(chidp);
+  }
+}
 
-  va_start(ap, argc);
-  return silc_notify_payload_encode(notify, argc, ap);
+/* Returns channel's invite and ban lists */
+
+void silc_server_announce_get_inviteban(SilcServer server,
+                                       SilcChannelEntry channel,
+                                       SilcBuffer *invite,
+                                       SilcBuffer *ban)
+{
+  SilcBuffer list, idp, idp2, tmp2;
+  SilcUInt32 type;
+  void *ptype;
+  SilcHashTableList htl;
+  const unsigned char a[1] = { 0x03 };
+
+  idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+
+  /* Encode invite list */
+  if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+    list = silc_buffer_alloc_size(2);
+    type = silc_hash_table_count(channel->invite_list);
+    SILC_PUT16_MSB(type, list->data);
+    silc_hash_table_list(channel->invite_list, &htl);
+    while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data,
+                                             silc_buffer_len(tmp2),
+                                             SILC_PTR_TO_32(ptype));
+    silc_hash_table_list_reset(&htl);
+
+    idp2 = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+    *invite =
+      silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_INVITE, 5,
+                                        idp->data, silc_buffer_len(idp),
+                                        channel->channel_name,
+                                        strlen(channel->channel_name),
+                                        idp2->data, silc_buffer_len(idp2),
+                                        a, 1,
+                                        list->data, silc_buffer_len(list));
+    silc_buffer_free(idp2);
+    silc_buffer_free(list);
+  }
+
+  /* Encode ban list */
+  if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+    list = silc_buffer_alloc_size(2);
+    type = silc_hash_table_count(channel->ban_list);
+    SILC_PUT16_MSB(type, list->data);
+    silc_hash_table_list(channel->ban_list, &htl);
+    while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data,
+                                             silc_buffer_len(tmp2),
+                                             SILC_PTR_TO_32(ptype));
+    silc_hash_table_list_reset(&htl);
+
+    *ban =
+      silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_BAN, 3,
+                                        idp->data, silc_buffer_len(idp),
+                                        a, 1,
+                                        list->data, silc_buffer_len(list));
+    silc_buffer_free(list);
+  }
+
+  silc_buffer_free(idp);
 }
 
 /* Returns assembled packets for channel users of the `channel'. */
 
 void silc_server_announce_get_channel_users(SilcServer server,
                                            SilcChannelEntry channel,
+                                           SilcBuffer *channel_modes,
                                            SilcBuffer *channel_users,
                                            SilcBuffer *channel_users_modes)
 {
   SilcChannelClientEntry chl;
-  SilcBuffer chidp, clidp;
-  SilcBuffer tmp;
+  SilcHashTableList htl;
+  SilcBuffer chidp, clidp, csidp;
+  SilcBuffer tmp, fkey = NULL, chpklist;
   int len;
-  unsigned char mode[4];
+  unsigned char mode[4], ulimit[4];
+  char *hmac;
+  void *tmp2;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* 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) {
+  csidp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+  chpklist = silc_server_get_channel_pk_list(server, channel, TRUE, FALSE);
+
+  /* CMODE notify */
+  SILC_PUT32_MSB(channel->mode, mode);
+  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+    SILC_PUT32_MSB(channel->user_limit, ulimit);
+  hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
+  if (channel->founder_key)
+    fkey = silc_public_key_payload_encode(channel->founder_key);
+  tmp =
+    silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
+                                      8, csidp->data,
+                                      silc_buffer_len(csidp),
+                                      mode, sizeof(mode),
+                                      NULL, 0,
+                                      hmac, hmac ? strlen(hmac) : 0,
+                                      channel->passphrase,
+                                      channel->passphrase ?
+                                      strlen(channel->passphrase) : 0,
+                                      fkey ? fkey->data : NULL,
+                                      fkey ? silc_buffer_len(fkey) : 0,
+                                      chpklist ? chpklist->data : NULL,
+                                      chpklist ?
+                                      silc_buffer_len(chpklist) : 0,
+                                      (channel->mode &
+                                       SILC_CHANNEL_MODE_ULIMIT ?
+                                       ulimit : NULL),
+                                      (channel->mode &
+                                       SILC_CHANNEL_MODE_ULIMIT ?
+                                       sizeof(ulimit) : 0));
+  len = silc_buffer_len(tmp);
+  tmp2 =
+    silc_buffer_realloc(*channel_modes,
+                       (*channel_modes ?
+                        silc_buffer_truelen((*channel_modes)) + len : len));
+  if (!tmp2)
+    return;
+  *channel_modes = tmp2;
+  silc_buffer_pull_tail(*channel_modes,
+                       ((*channel_modes)->end -
+                        (*channel_modes)->data));
+  silc_buffer_put(*channel_modes, tmp->data, silc_buffer_len(tmp));
+  silc_buffer_pull(*channel_modes, len);
+  silc_buffer_free(tmp);
+  silc_buffer_free(fkey);
+  fkey = NULL;
+
+  /* Now find all users on the 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);
 
+    SILC_LOG_DEBUG(("JOIN Client %s", silc_id_render(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 - 
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2,
+                                            clidp->data,
+                                            silc_buffer_len(clidp),
+                                            chidp->data,
+                                            silc_buffer_len(chidp));
+    len = silc_buffer_len(tmp);
+    tmp2 =
+      silc_buffer_realloc(*channel_users,
+                         (*channel_users ?
+                          silc_buffer_truelen((*channel_users)) + len : len));
+    if (!tmp2)
+      return;
+    *channel_users = tmp2;
+    silc_buffer_pull_tail(*channel_users,
+                         ((*channel_users)->end -
                           (*channel_users)->data));
-    
-    silc_buffer_put(*channel_users, tmp->data, tmp->len);
+
+    silc_buffer_put(*channel_users, tmp->data, silc_buffer_len(tmp));
     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 - 
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && channel->founder_key)
+      fkey = silc_public_key_payload_encode(channel->founder_key);
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
+                                            4, csidp->data,
+                                            silc_buffer_len(csidp),
+                                            mode, sizeof(mode),
+                                            clidp->data,
+                                            silc_buffer_len(clidp),
+                                            fkey ? fkey->data : NULL,
+                                            fkey ? silc_buffer_len(fkey) : 0);
+    len = silc_buffer_len(tmp);
+    tmp2 =
+      silc_buffer_realloc(*channel_users_modes,
+                         (*channel_users_modes ?
+                          silc_buffer_truelen((*channel_users_modes)) +
+                          len : len));
+    if (!tmp2)
+      return;
+    *channel_users_modes = tmp2;
+    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_put(*channel_users_modes, tmp->data, silc_buffer_len(tmp));
     silc_buffer_pull(*channel_users_modes, len);
     silc_buffer_free(tmp);
-
+    silc_buffer_free(fkey);
+    fkey = NULL;
     silc_buffer_free(clidp);
   }
+  silc_hash_table_list_reset(&htl);
   silc_buffer_free(chidp);
+  silc_buffer_free(csidp);
 }
 
 /* Returns assembled packets for all channels and users on those channels
@@ -3307,173 +4503,401 @@ void silc_server_announce_get_channel_users(SilcServer server,
 void silc_server_announce_get_channels(SilcServer server,
                                       SilcIDList id_list,
                                       SilcBuffer *channels,
+                                      SilcBuffer **channel_modes,
                                       SilcBuffer *channel_users,
-                                      SilcBuffer *channel_users_modes)
+                                      SilcBuffer **channel_users_modes,
+                                      SilcUInt32 *channel_users_modes_c,
+                                      SilcBuffer **channel_topics,
+                                      SilcBuffer **channel_invites,
+                                      SilcBuffer **channel_bans,
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time)
 {
-  SilcIDCacheList list;
+  SilcList list;
   SilcIDCacheEntry id_cache;
   SilcChannelEntry channel;
-  unsigned char *cid;
-  uint32 id_len;
-  uint16 name_len;
+  unsigned char cid[32];
+  SilcUInt32 id_len;
+  SilcUInt16 name_len;
   int len;
+  int i = *channel_users_modes_c;
+  void *tmp;
+  SilcBool 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_list_first(list, &id_cache)) {
-      while (id_cache) {
-       channel = (SilcChannelEntry)id_cache->context;
-       
-       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);
+  if (silc_idcache_get_all(id_list->channels, &list)) {
+    silc_list_start(list);
+    while ((id_cache = silc_list_get(list))) {
+      channel = (SilcChannelEntry)id_cache->context;
+
+      if (creation_time && channel->created < creation_time)
+       announce = FALSE;
+      else
+       announce = TRUE;
+
+      SILC_LOG_DEBUG(("Announce Channel ID %s",
+                     silc_id_render(channel->id, SILC_ID_CHANNEL)));
 
+      silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
+      name_len = strlen(channel->channel_name);
+
+      if (announce) {
        len = 4 + name_len + id_len + 4;
-       *channels = 
-         silc_buffer_realloc(*channels, 
-                             (*channels ? (*channels)->truelen + len : len));
-       silc_buffer_pull_tail(*channels, 
+       tmp =
+         silc_buffer_realloc(*channels,
+                             (*channels ?
+                              silc_buffer_truelen((*channels)) +
+                              len : len));
+       if (!tmp)
+         break;
+       *channels = tmp;
+
+       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, 
+                          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_XNSTRING(cid, id_len),
                           SILC_STR_UI_INT(channel->mode),
                           SILC_STR_END);
        silc_buffer_pull(*channels, len);
+      }
 
+      if (creation_time && channel->updated < creation_time)
+       announce = FALSE;
+      else
+       announce = TRUE;
+
+      if (announce) {
+       /* Channel user modes */
+       tmp = silc_realloc(*channel_users_modes,
+                           sizeof(**channel_users_modes) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_users_modes = tmp;
+       (*channel_users_modes)[i] = NULL;
+       tmp = silc_realloc(*channel_modes,
+                          sizeof(**channel_modes) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_modes = tmp;
+       (*channel_modes)[i] = NULL;
+       tmp = silc_realloc(*channel_ids,
+                          sizeof(**channel_ids) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_ids = tmp;
+       (*channel_ids)[i] = NULL;
        silc_server_announce_get_channel_users(server, channel,
+                                              &(*channel_modes)[i],
                                               channel_users,
-                                              channel_users_modes);
-
-       silc_free(cid);
+                                              &(*channel_users_modes)[i]);
+       (*channel_ids)[i] = channel->id;
 
-       if (!silc_idcache_list_next(list, &id_cache))
+       /* Channel's topic */
+       tmp = silc_realloc(*channel_topics,
+                          sizeof(**channel_topics) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_topics = tmp;
+       (*channel_topics)[i] = NULL;
+       silc_server_announce_get_channel_topic(server, channel,
+                                              &(*channel_topics)[i]);
+
+       /* Channel's invite and ban list */
+       tmp = silc_realloc(*channel_invites,
+                          sizeof(**channel_invites) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_invites = tmp;
+       (*channel_invites)[i] = NULL;
+       tmp = silc_realloc(*channel_bans,
+                          sizeof(**channel_bans) * (i + 1));
+       if (!tmp)
          break;
+       *channel_bans = tmp;
+       (*channel_bans)[i] = NULL;
+       silc_server_announce_get_inviteban(server, channel,
+                                          &(*channel_invites)[i],
+                                          &(*channel_bans)[i]);
+
+       (*channel_users_modes_c)++;
+
+       i++;
       }
     }
-
-    silc_idcache_list_free(list);
   }
 }
 
 /* This function is used to announce our existing channels to our router
    when we've connected to it. This also announces the users on the
-   channels to the router. */
-
-void silc_server_announce_channels(SilcServer server)
+   channels to the router. If the `creation_time' is non-zero only the
+   channels that was created after the `creation_time' are announced.
+   Note that the channel users are still announced even if the `creation_time'
+   was provided. */
+
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time,
+                                  SilcPacketStream remote)
 {
-  SilcBuffer channels = NULL, channel_users = NULL, channel_users_modes = NULL;
+  SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
+  SilcBuffer *channel_users_modes = NULL;
+  SilcBuffer *channel_topics = NULL;
+  SilcBuffer *channel_invites = NULL;
+  SilcBuffer *channel_bans = NULL;
+  SilcUInt32 channel_users_modes_c = 0;
+  SilcChannelID **channel_ids = NULL;
 
   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,
-                                   &channel_users_modes);
+                                   &channels, &channel_modes,
+                                   &channel_users,
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_topics,
+                                   &channel_invites,
+                                   &channel_bans,
+                                   &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
-  silc_server_announce_get_channels(server, server->global_list,
-                                   &channels, &channel_users,
-                                   &channel_users_modes);
+  if (server->server_type != SILC_SERVER)
+    silc_server_announce_get_channels(server, server->global_list,
+                                     &channels, &channel_modes,
+                                     &channel_users,
+                                     &channel_users_modes,
+                                     &channel_users_modes_c,
+                                     &channel_topics,
+                                     &channel_invites,
+                                     &channel_bans,
+                                     &channel_ids, creation_time);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
-    SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
+    SILC_LOG_HEXDUMP(("channels"), channels->data, silc_buffer_len(channels));
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
-                           channels->data, channels->len,
-                           FALSE);
+                           channels->data, silc_buffer_len(channels));
 
     silc_buffer_free(channels);
   }
 
   if (channel_users) {
     silc_buffer_push(channel_users, channel_users->data - channel_users->head);
-    SILC_LOG_HEXDUMP(("channel users"), channel_users->data, 
-                    channel_users->len);
+    SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
+                    silc_buffer_len(channel_users));
 
     /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, remote,
                            SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
-                           channel_users->data, channel_users->len,
-                           FALSE);
+                           channel_users->data, silc_buffer_len(channel_users));
 
     silc_buffer_free(channel_users);
   }
 
+  if (channel_modes) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_modes[i])
+        continue;
+      silc_buffer_push(channel_modes[i],
+                      channel_modes[i]->data -
+                      channel_modes[i]->head);
+      SILC_LOG_HEXDUMP(("channel modes"), channel_modes[i]->data,
+                      silc_buffer_len(channel_modes[i]));
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_modes[i]->data,
+                                  silc_buffer_len(channel_modes[i]));
+      silc_buffer_free(channel_modes[i]);
+    }
+    silc_free(channel_modes);
+  }
+
   if (channel_users_modes) {
-    silc_buffer_push(channel_users_modes, 
-                    channel_users_modes->data - channel_users_modes->head);
-    SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes->data, 
-                    channel_users_modes->len);
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_users_modes[i])
+        continue;
+      silc_buffer_push(channel_users_modes[i],
+                      channel_users_modes[i]->data -
+                      channel_users_modes[i]->head);
+      SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data,
+                      silc_buffer_len(channel_users_modes[i]));
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_users_modes[i]->data,
+                                  silc_buffer_len(channel_users_modes[i]));
+      silc_buffer_free(channel_users_modes[i]);
+    }
+    silc_free(channel_users_modes);
+  }
 
-    /* Send the packet */
-    silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
-                           channel_users_modes->data, 
-                           channel_users_modes->len,
-                           FALSE);
+  if (channel_topics) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_topics[i])
+       continue;
+
+      silc_buffer_push(channel_topics[i],
+                      channel_topics[i]->data -
+                      channel_topics[i]->head);
+      SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data,
+                      silc_buffer_len(channel_topics[i]));
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_topics[i]->data,
+                                  silc_buffer_len(channel_topics[i]));
+      silc_buffer_free(channel_topics[i]);
+    }
+    silc_free(channel_topics);
+  }
+
+  if (channel_invites) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_invites[i])
+       continue;
+
+      silc_buffer_push(channel_invites[i],
+                      channel_invites[i]->data -
+                      channel_invites[i]->head);
+      SILC_LOG_HEXDUMP(("channel invite list"), channel_invites[i]->data,
+                      silc_buffer_len(channel_invites[i]));
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_invites[i]->data,
+                                  silc_buffer_len(channel_invites[i]));
+      silc_buffer_free(channel_invites[i]);
+    }
+    silc_free(channel_invites);
+  }
+
+  if (channel_bans) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_bans[i])
+       continue;
 
-    silc_buffer_free(channel_users_modes);
+      silc_buffer_push(channel_bans[i],
+                      channel_bans[i]->data -
+                      channel_bans[i]->head);
+      SILC_LOG_HEXDUMP(("channel ban list"), channel_bans[i]->data,
+                      silc_buffer_len(channel_bans[i]));
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_bans[i]->data,
+                                  silc_buffer_len(channel_bans[i]));
+      silc_buffer_free(channel_bans[i]);
+    }
+    silc_free(channel_bans);
   }
+
+  silc_free(channel_ids);
 }
 
-/* Failure timeout callback. If this is called then we will immediately
-   process the received failure. We always process the failure with timeout
-   since we do not want to blindly trust to received failure packets. 
-   This won't be called (the timeout is cancelled) if the failure was
-   bogus (it is bogus if remote does not close the connection after sending
-   the failure). */
+/* Announces WATCH list. */
 
-SILC_TASK_CALLBACK(silc_server_failure_callback)
+void silc_server_announce_watches(SilcServer server,
+                                 SilcPacketStream remote)
 {
-  SilcServerFailureContext f = (SilcServerFailureContext)context;
+  SilcHashTableList htl;
+  SilcBuffer buffer, idp, args, pkp;
+  SilcClientEntry client;
+  void *key;
 
-  if (f->sock->protocol) {
-    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-    f->sock->protocol->execute(f->server->timeout_queue, 0,
-                              f->sock->protocol, f->sock->sock, 0, 0);
-  }
+  SILC_LOG_DEBUG(("Announcing watch list"));
+
+  /* XXX because way we save the nicks (hash) we cannot announce them. */
+
+  /* XXX we should send all public keys in one command if client is
+     watching more than one key */
+  silc_hash_table_list(server->watcher_list_pk, &htl);
+  while (silc_hash_table_get(&htl, &key, (void *)&client)) {
+    if (!client || !client->id)
+      continue;
 
-  silc_free(f);
+    server->stat.commands_sent++;
+
+    idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    args = silc_buffer_alloc_size(2);
+    silc_buffer_format(args,
+                      SILC_STR_UI_SHORT(1),
+                      SILC_STR_END);
+    pkp = silc_public_key_payload_encode(key);
+    args = silc_argument_payload_encode_one(args, pkp->data,
+                                           silc_buffer_len(pkp), 0x00);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH,
+                                           ++server->cmd_ident, 2,
+                                           1, idp->data, silc_buffer_len(idp),
+                                           4, args->data,
+                                           silc_buffer_len(args));
+
+    /* Send command */
+    silc_server_packet_send(server, remote, SILC_PACKET_COMMAND, 0,
+                           buffer->data, silc_buffer_len(buffer));
+
+    silc_buffer_free(pkp);
+    silc_buffer_free(args);
+    silc_buffer_free(idp);
+    silc_buffer_free(buffer);
+  }
+  silc_hash_table_list_reset(&htl);
 }
 
 /* Assembles user list and users mode list from the `channel'. */
 
-void silc_server_get_users_on_channel(SilcServer server,
-                                     SilcChannelEntry channel,
-                                     SilcBuffer *user_list,
-                                     SilcBuffer *mode_list,
-                                     uint32 *user_count)
+SilcBool silc_server_get_users_on_channel(SilcServer server,
+                                         SilcChannelEntry channel,
+                                         SilcBuffer *user_list,
+                                         SilcBuffer *mode_list,
+                                         SilcUInt32 *user_count)
 {
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   SilcBuffer idp;
-  uint32 list_count = 0;
-
-  /* XXX rewrite - this does not support IPv6 based Client ID's. */
-
-  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));
-  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) {
+  SilcUInt32 list_count = 0, len = 0;
+
+  if (!silc_hash_table_count(channel->user_list))
+    return FALSE;
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+    len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
+  silc_hash_table_list_reset(&htl);
+
+  client_id_list = silc_buffer_alloc(len);
+  client_mode_list =
+    silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
+  silc_buffer_pull_tail(client_id_list, silc_buffer_truelen(client_id_list));
+  silc_buffer_pull_tail(client_mode_list,
+                       silc_buffer_truelen(client_mode_list));
+
+  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);
-    silc_buffer_pull(client_id_list, idp->len);
+    silc_buffer_put(client_id_list, idp->data, silc_buffer_len(idp));
+    silc_buffer_pull(client_id_list, silc_buffer_len(idp));
     silc_buffer_free(idp);
 
     /* Client's mode on channel */
@@ -3482,94 +4906,212 @@ void silc_server_get_users_on_channel(SilcServer server,
 
     list_count++;
   }
-  silc_buffer_push(client_id_list, 
+  silc_hash_table_list_reset(&htl);
+  silc_buffer_push(client_id_list,
                   client_id_list->data - client_id_list->head);
-  silc_buffer_push(client_mode_list, 
+  silc_buffer_push(client_mode_list,
                   client_mode_list->data - client_mode_list->head);
 
   *user_list = client_id_list;
   *mode_list = client_mode_list;
   *user_count = list_count;
+  return TRUE;
 }
 
 /* Saves users and their modes to the `channel'. */
 
 void silc_server_save_users_on_channel(SilcServer server,
-                                      SilcSocketConnection sock,
+                                      SilcPacketStream sock,
                                       SilcChannelEntry channel,
                                       SilcClientID *noadd,
                                       SilcBuffer user_list,
                                       SilcBuffer mode_list,
-                                      uint32 user_count)
+                                      SilcUInt32 user_count)
 {
   int i;
+  SilcUInt16 idp_len;
+  SilcUInt32 mode;
+  SilcID id;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache;
+  SilcChannelClientEntry chl;
 
-  /* Cache the received Client ID's and modes. This cache expires
-     whenever server sends notify message to channel. It means two things;
-     some user has joined or leaved the channel. XXX TODO! */
-  for (i = 0; i < user_count; i++) {
-    uint16 idp_len;
-    uint32 mode;
-    SilcClientID *client_id;
-    SilcClientEntry client;
+  SILC_LOG_DEBUG(("Saving %d users on %s channel", user_count,
+                 channel->channel_name));
 
+  for (i = 0; i < user_count; i++) {
     /* Client ID */
     SILC_GET16_MSB(idp_len, user_list->data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(user_list->data, idp_len);
-    silc_buffer_pull(user_list, idp_len);
-    if (!client_id)
+    if (!silc_id_payload_parse_id(user_list->data, idp_len, &id))
       continue;
+    silc_buffer_pull(user_list, idp_len);
 
     /* Mode */
     SILC_GET32_MSB(mode, mode_list->data);
     silc_buffer_pull(mode_list, 4);
 
-    if (noadd && SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
-      silc_free(client_id);
+    if (noadd && SILC_ID_CLIENT_COMPARE(&id.u.client_id, noadd))
       continue;
-    }
-    
+
+    cache = NULL;
+
     /* Check if we have this client cached already. */
-    client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                          NULL);
+    client = silc_idlist_find_client_by_id(server->local_list,
+                                          &id.u.client_id,
+                                          server->server_type, &cache);
     if (!client)
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, NULL);
+      client = silc_idlist_find_client_by_id(server->global_list,
+                                            &id.u.client_id,
+                                            server->server_type, &cache);
     if (!client) {
       /* If router did not find such Client ID in its lists then this must
         be bogus client or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER) {
-       silc_free(client_id);
+      if (server->server_type != SILC_SERVER)
        continue;
-      }
 
       /* We don't have that client anywhere, add it. The client is added
-        to global list since server didn't have it in the lists so it must be 
+        to global list since server didn't have it in the lists so it must be
         global. */
-      client = silc_idlist_add_client(server->global_list, NULL, 0, NULL, 
-                                     NULL, 
-                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
-                                     sock->user_data, NULL);
+      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+                                     silc_id_dup(&id.u.client_id,
+                                                 SILC_ID_CLIENT),
+                                     silc_packet_get_context(sock),
+                                     NULL);
       if (!client) {
-       silc_free(client_id);
+       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
        continue;
       }
 
-      client->data.registered = TRUE;
+      client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
     }
 
-    silc_free(client_id);
+    if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      SILC_LOG_ERROR(("Attempting to add unregistered client to channel ",
+                     "%s", channel->channel_name));
+      continue;
+    }
 
-    if (!silc_server_client_on_channel(client, channel)) {
+    if (!silc_server_client_on_channel(client, channel, &chl)) {
       /* Client was not on the channel, add it. */
-      SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+      chl = silc_calloc(1, sizeof(*chl));
       chl->client = client;
       chl->mode = mode;
       chl->channel = channel;
-      silc_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);
+      channel->user_count++;
+    } else {
+      /* Update mode */
+      chl->mode = mode;
+    }
+  }
+}
+
+/* Saves channels and channels user modes to the `client'.  Removes
+   the client from those channels that are not sent in the list but
+   it has joined. */
+
+void silc_server_save_user_channels(SilcServer server,
+                                   SilcPacketStream sock,
+                                   SilcClientEntry client,
+                                   SilcBuffer channels,
+                                   SilcBuffer channels_user_modes)
+{
+  SilcDList ch;
+  SilcUInt32 *chumodes;
+  SilcChannelPayload entry;
+  SilcChannelEntry channel;
+  SilcChannelID channel_id;
+  SilcChannelClientEntry chl;
+  SilcHashTable ht = NULL;
+  SilcHashTableList htl;
+  char *name;
+  int i = 0;
+
+  if (!channels || !channels_user_modes ||
+      !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
+    goto out;
+
+  ch = silc_channel_payload_parse_list(channels->data,
+                                      silc_buffer_len(channels));
+  if (ch && silc_get_mode_list(channels_user_modes, silc_dlist_count(ch),
+                              &chumodes)) {
+    ht = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
+                              NULL, NULL, NULL, TRUE);
+    silc_dlist_start(ch);
+    while ((entry = silc_dlist_get(ch)) != SILC_LIST_END) {
+      /* Check if we have this channel, and add it if we don't have it.
+        Also add the client on the channel unless it is there already. */
+      if (!silc_channel_get_id_parse(entry, &channel_id))
+       continue;
+      channel = silc_idlist_find_channel_by_id(server->local_list,
+                                              &channel_id, NULL);
+      if (!channel)
+       channel = silc_idlist_find_channel_by_id(server->global_list,
+                                                &channel_id, NULL);
+      if (!channel) {
+       if (server->server_type != SILC_SERVER) {
+         i++;
+         continue;
+       }
+
+       /* We don't have that channel anywhere, add it. */
+       name = silc_channel_get_name(entry, NULL);
+       channel = silc_idlist_add_channel(server->global_list, strdup(name), 0,
+                                         silc_id_dup(&channel_id,
+                                                     SILC_ID_CHANNEL),
+                                         server->router, NULL, NULL, 0);
+       if (!channel) {
+         i++;
+         continue;
+       }
+      }
+
+      channel->mode = silc_channel_get_mode(entry);
+
+      /* Add the client on the channel */
+      if (!silc_server_client_on_channel(client, channel, &chl)) {
+       chl = silc_calloc(1, sizeof(*chl));
+       chl->client = client;
+       chl->mode = chumodes[i++];
+       chl->channel = channel;
+       silc_hash_table_add(channel->user_list, chl->client, chl);
+       silc_hash_table_add(client->channels, chl->channel, chl);
+       channel->user_count++;
+      } else {
+       /* Update mode */
+       chl->mode = chumodes[i++];
+      }
+
+      silc_hash_table_add(ht, channel, channel);
+    }
+    silc_channel_payload_list_free(ch);
+    silc_free(chumodes);
+  }
+
+ out:
+  /* Go through the list again and remove client from channels that
+     are no part of the list. */
+  if (ht) {
+    silc_hash_table_list(client->channels, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+      if (!silc_hash_table_find(ht, chl->channel, NULL, NULL)) {
+       silc_hash_table_del(chl->channel->user_list, chl->client);
+       silc_hash_table_del(chl->client->channels, chl->channel);
+       silc_free(chl);
+      }
     }
+    silc_hash_table_list_reset(&htl);
+    silc_hash_table_free(ht);
+  } else {
+    silc_hash_table_list(client->channels, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+      silc_hash_table_del(chl->channel->user_list, chl->client);
+      silc_hash_table_del(chl->client->channels, chl->channel);
+      silc_free(chl);
+    }
+    silc_hash_table_list_reset(&htl);
   }
 }
 
@@ -3578,43 +5120,43 @@ void silc_server_save_users_on_channel(SilcServer server,
    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,
-                                                 uint32 id_len,
-                                                 SilcClientID *client_id,
-                                                 SilcIDListData *idata)
+SilcPacketStream
+silc_server_get_client_route(SilcServer server,
+                            unsigned char *id_data,
+                            SilcUInt32 id_len,
+                            SilcClientID *client_id,
+                            SilcIDListData *idata,
+                            SilcClientEntry *client_entry)
 {
-  SilcClientID *id;
+  SilcClientID *id, clid;
   SilcClientEntry client;
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (client_entry)
+    *client_entry = NULL;
+
   /* Decode destination Client ID */
   if (!client_id) {
-    id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
-    if (!id) {
-      SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+    if (!silc_id_str2id(id_data, id_len, SILC_ID_CLIENT, &clid, sizeof(clid)))
       return NULL;
-    }
+    id = silc_id_dup(&clid, SILC_ID_CLIENT);
   } else {
     id = silc_id_dup(client_id, SILC_ID_CLIENT);
   }
 
   /* 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. */
+      /* 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;
@@ -3623,31 +5165,34 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
     /* Seems that client really is directly connected to us */
     if (idata)
       *idata = (SilcIDListData)client;
+    if (client_entry)
+      *client_entry = client;
     return client->connection;
   }
 
   /* Destination belongs to someone not in this server. If we are normal
      server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
+  if (server->server_type != SILC_ROUTER && !server->standalone) {
     silc_free(id);
     if (idata)
       *idata = (SilcIDListData)server->router;
-    return server->router->connection;
+    return SILC_PRIMARY_ROUTE(server);
   }
 
-  /* We are router and we will perform route lookup for the destination 
+  /* We are router and we will perform route lookup for the destination
      and send the packet to fastest route. */
   if (server->server_type == SILC_ROUTER && !server->standalone) {
     /* Check first that the ID is valid */
-    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+    client = silc_idlist_find_client_by_id(server->global_list, id,
+                                          TRUE, NULL);
     if (client) {
-      SilcSocketConnection dst_sock;
+      SilcPacketStream dst_sock;
 
       dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
 
       silc_free(id);
-      if (idata)
-       *idata = (SilcIDListData)dst_sock->user_data;
+      if (idata && dst_sock)
+       *idata = silc_packet_get_context(dst_sock);
       return dst_sock;
     }
   }
@@ -3660,157 +5205,96 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
    Secret channels are not put to the list. */
 
 SilcBuffer silc_server_get_client_channel_list(SilcServer server,
-                                              SilcClientEntry client)
+                                              SilcClientEntry client,
+                                              SilcBool get_private,
+                                              SilcBool get_secret,
+                                              SilcBuffer *user_mode_list)
 {
   SilcBuffer buffer = NULL;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
-  unsigned char *cid;
-  uint32 id_len;
-  uint16 name_len;
+  SilcHashTableList htl;
+  unsigned char cid[32];
+  SilcUInt32 id_len;
+  SilcUInt16 name_len;
   int len;
 
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+  if (user_mode_list)
+    *user_mode_list = NULL;
+
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     channel = chl->channel;
 
-    if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+    if (channel->mode & SILC_CHANNEL_MODE_SECRET && !get_secret)
+      continue;
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVATE && !get_private)
       continue;
 
-    cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-    id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+    silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
     name_len = strlen(channel->channel_name);
-    
+
     len = 4 + name_len + id_len + 4;
-    buffer = silc_buffer_realloc(buffer, 
-                                (buffer ? (buffer)->truelen + len : len));
-    silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+    buffer = silc_buffer_realloc(buffer,
+                                (buffer ?
+                                 silc_buffer_truelen(buffer) + len : len));
+    silc_buffer_pull_tail(buffer, (buffer->end - buffer->data));
     silc_buffer_format(buffer,
                       SILC_STR_UI_SHORT(name_len),
-                      SILC_STR_UI_XNSTRING(channel->channel_name, 
-                                           name_len),
+                      SILC_STR_DATA(channel->channel_name, name_len),
                       SILC_STR_UI_SHORT(id_len),
-                      SILC_STR_UI_XNSTRING(cid, id_len),
-                      SILC_STR_UI_INT(chl->mode), /* Client's mode */
+                      SILC_STR_DATA(cid, id_len),
+                      SILC_STR_UI_INT(chl->channel->mode),
                       SILC_STR_END);
     silc_buffer_pull(buffer, len);
-    silc_free(cid);
+
+    if (user_mode_list) {
+      *user_mode_list =
+       silc_buffer_realloc(*user_mode_list,
+                           (*user_mode_list ?
+                            silc_buffer_truelen((*user_mode_list)) + 4 : 4));
+      silc_buffer_pull_tail(*user_mode_list, ((*user_mode_list)->end -
+                                             (*user_mode_list)->data));
+      SILC_PUT32_MSB(chl->mode, (*user_mode_list)->data);
+      silc_buffer_pull(*user_mode_list, 4);
+    }
   }
+  silc_hash_table_list_reset(&htl);
 
   if (buffer)
     silc_buffer_push(buffer, buffer->data - buffer->head);
+  if (user_mode_list && *user_mode_list)
+    silc_buffer_push(*user_mode_list, ((*user_mode_list)->data -
+                                      (*user_mode_list)->head));
 
   return buffer;
 }
 
-/* Finds client entry by Client ID and if it is not found then resolves
-   it using WHOIS command. */
-
-SilcClientEntry silc_server_get_client_resolve(SilcServer server,
-                                              SilcClientID *client_id)
-{
-  SilcClientEntry client;
-
-  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
-  if (!client) {
-    client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, NULL);
-    if (!client && server->server_type == SILC_ROUTER)
-      return NULL;
-  }
-
-  if (!client && server->standalone)
-    return NULL;
-
-  if (!client || !client->nickname || !client->username) {
-    SilcBuffer buffer, idp;
-
-    idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
-                                           ++server->cmd_ident, 1,
-                                           3, idp->data, idp->len);
-    silc_server_packet_send(server, client ? client->router->connection :
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, 0,
-                           buffer->data, buffer->len, FALSE);
-    silc_buffer_free(idp);
-    silc_buffer_free(buffer);
-  }
-
-  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 */
-  protocol->execute(server->timeout_queue, 0, protocol, 
-                   sock->sock, 0, 0);
-
-  /* Re-register re-key timeout */
-  silc_task_register(server->timeout_queue, 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. */
+/* Task callback used to retrieve network statistical information from
+   router server once in a while. */
 
-SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
+SILC_TASK_CALLBACK(silc_server_get_stats)
 {
-  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_protocol_cancel(server->timeout_queue, 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);
-    silc_free(ctx);
-    return;
+  SilcServer server = (SilcServer)context;
+  SilcBuffer idp, packet;
+
+  if (!server->standalone) {
+    SILC_LOG_DEBUG(("Retrieving stats from router"));
+    server->stat.commands_sent++;
+    idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+    if (idp) {
+      packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+                                             ++server->cmd_ident, 1,
+                                             1, idp->data,
+                                             silc_buffer_len(idp));
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                             SILC_PACKET_COMMAND, 0, packet->data,
+                             silc_buffer_len(packet));
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+    }
   }
 
-  /* 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);
+  silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
+                                server, 120, 0);
 }