updates.
[silc.git] / apps / silcd / server.c
index 149b34b963574d14593861624ed5396ead137537..4892cd065864cae453981326a1b592d73dc6438b 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 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
@@ -28,6 +28,7 @@
 #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);
@@ -37,8 +38,8 @@ 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);
-
-extern char *server_version;
+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
@@ -53,9 +54,10 @@ int silc_server_alloc(SilcServer *new_server)
 
   server = silc_calloc(1, sizeof(*server));
   server->server_type = SILC_SERVER;
-  server->standalone = FALSE;
+  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
@@ -71,7 +73,9 @@ int silc_server_alloc(SilcServer *new_server)
 void silc_server_free(SilcServer server)
 {
   if (server) {
+#ifdef SILC_SIM
     SilcSimContext *sim;
+#endif
 
     if (server->local_list)
       silc_free(server->local_list);
@@ -80,13 +84,20 @@ void silc_server_free(SilcServer server)
     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_math_primegen_uninit(); /* XXX */
     silc_free(server);
   }
 }
@@ -103,24 +114,46 @@ int silc_server_init(SilcServer server)
   int *sock = NULL, sock_count = 0, i;
   SilcServerID *id;
   SilcServerEntry id_entry;
+  SilcIDListPurge purge;
 
   SILC_LOG_DEBUG(("Initializing server"));
   assert(server);
   assert(server->config);
 
+  /* Set public and private keys */
+  if (!server->config->server_keys ||
+      !server->config->server_keys->public_key || 
+      !server->config->server_keys->private_key) {
+    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
+    return FALSE;
+  }
+  server->public_key = server->config->server_keys->public_key;
+  server->private_key = server->config->server_keys->private_key;
+
+  /* XXX After server is made as Silc Server Library this can be given
+     as argument, for now this is hard coded */
+  server->params = silc_calloc(1, sizeof(*server->params));
+  server->params->retry_count = SILC_SERVER_RETRY_COUNT;
+  server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN;
+  server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
+  server->params->retry_keep_trying = FALSE;
+  server->params->protocol_timeout = 60;
+  server->params->require_reverse_mapping = FALSE;
+
   /* Set log files where log message should be saved. */
   server->config->server = server;
-  silc_config_server_setlogfiles(server->config);
+  silc_server_config_setlogfiles(server->config);
  
   /* Register all configured ciphers, PKCS and hash functions. */
-  silc_config_server_register_ciphers(server->config);
-  silc_config_server_register_pkcs(server->config);
-  silc_config_server_register_hashfuncs(server->config);
+  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_math_primegen_init(); /* XXX */
+  silc_rng_global_init(server->rng);
 
   /* Initialize hash functions for server to use */
   silc_hash_alloc("md5", &server->md5hash);
@@ -129,58 +162,6 @@ int silc_server_init(SilcServer server)
   /* Initialize none cipher */
   silc_cipher_alloc("none", &server->none_cipher);
 
-  /* XXXXX Generate RSA key pair */
-  {
-    unsigned char *public_key;
-    unsigned char *private_key;
-    unsigned int pk_len, prv_len;
-    struct stat st;
-
-    if (stat("pubkey.pub", &st) < 0 && stat("privkey.prv", &st) < 0) {
-
-      if (silc_pkcs_alloc("rsa", &server->pkcs) == FALSE) {
-       SILC_LOG_ERROR(("Could not create RSA key pair"));
-       goto err0;
-      }
-      
-      if (server->pkcs->pkcs->init(server->pkcs->context, 
-                                  1024, server->rng) == FALSE) {
-       SILC_LOG_ERROR(("Could not generate RSA key pair"));
-       goto err0;
-      }
-      
-      public_key = server->pkcs->pkcs->get_public_key(server->pkcs->context,
-                                                     &pk_len);
-      private_key = server->pkcs->pkcs->get_private_key(server->pkcs->context,
-                                                       &prv_len);
-      
-      SILC_LOG_HEXDUMP(("public key"), public_key, pk_len);
-      SILC_LOG_HEXDUMP(("private key"), private_key, prv_len);
-      
-      server->public_key = 
-       silc_pkcs_public_key_alloc("rsa", "UN=root, HN=dummy",
-                                  public_key, pk_len);
-      server->private_key = 
-       silc_pkcs_private_key_alloc("rsa", private_key, prv_len);
-      
-      /* XXX Save keys */
-      silc_pkcs_save_public_key("pubkey.pub", server->public_key,
-                               SILC_PKCS_FILE_PEM);
-      silc_pkcs_save_private_key("privkey.prv", server->private_key, NULL,
-                                SILC_PKCS_FILE_BIN);
-
-      memset(public_key, 0, pk_len);
-      memset(private_key, 0, prv_len);
-      silc_free(public_key);
-      silc_free(private_key);
-    } else {
-      silc_pkcs_load_public_key("pubkey.pub", &server->public_key,
-                               SILC_PKCS_FILE_PEM);
-      silc_pkcs_load_private_key("privkey.prv", &server->private_key,
-                                SILC_PKCS_FILE_BIN);
-    }
-  }
-
   /* 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. */
@@ -200,18 +181,18 @@ int silc_server_init(SilcServer server)
   }
 
   /* Initialize ID caches */
-  server->local_list->clients = silc_idcache_alloc(0);
-  server->local_list->servers = silc_idcache_alloc(0);
-  server->local_list->channels = silc_idcache_alloc(0);
-
-  /* XXX for now 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. The XXX can be remoevd later if this is the way we are
-     going to do this in the normal server as well. */
-  server->global_list->clients = silc_idcache_alloc(0);
-  server->global_list->servers = silc_idcache_alloc(0);
-  server->global_list->channels = silc_idcache_alloc(0);
+  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 
@@ -234,6 +215,8 @@ int silc_server_init(SilcServer server)
     }
     
     server->id = id;
+    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
+    server->id_string_len = silc_id_get_len(SILC_ID_SERVER);
     server->id_type = SILC_ID_SERVER;
     server->server_name = server->config->server_info->server_name;
 
@@ -244,9 +227,7 @@ int silc_server_init(SilcServer server)
     id_entry = 
       silc_idlist_add_server(server->local_list,
                             server->config->server_info->server_name,
-                            server->server_type, server->id, NULL,
-                            server->send_key, server->receive_key,
-                            NULL, NULL, NULL, NULL);
+                            server->server_type, server->id, NULL, NULL);
     if (!id_entry) {
       SILC_LOG_ERROR(("Could not add ourselves to cache"));
       goto err0;
@@ -256,10 +237,25 @@ int silc_server_init(SilcServer server)
        is sent as argument for fast referencing in the future. */
     silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, 
                      &newsocket);
-    if (!newsocket)
-      goto err0;
 
     server->sockets[sock[i]] = newsocket;
+    
+    /* Perform name and address lookups to resolve the listenning address
+       and port. */
+    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
+                                    &newsocket->ip)) {
+      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+         !newsocket->ip) {
+       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+                       newsocket->hostname ? newsocket->hostname :
+                       newsocket->ip ? newsocket->ip : ""));
+       server->stat.conn_failures++;
+       goto err0;
+      }
+      if (!newsocket->hostname)
+       newsocket->hostname = strdup(newsocket->ip);
+    }
+    newsocket->port = silc_net_get_local_port(sock[i]);
 
     /* Put the allocated socket pointer also to the entry allocated above 
        for fast back-referencing to the socket list. */
@@ -296,25 +292,52 @@ int silc_server_init(SilcServer server)
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  if (silc_task_register(server->timeout_queue, sock[0], 
-                        silc_server_connect_to_router,
-                        (void *)server, 0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL) == NULL) {
-    goto err2;
-  }
+  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;
 
+  /* 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);
+
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
   return TRUE;
 
- err2:
   silc_task_queue_free(server->timeout_queue);
  err1:
   silc_task_queue_free(server->io_queue);
@@ -325,6 +348,110 @@ int silc_server_init(SilcServer server)
   return FALSE;
 }
 
+/* Fork server to background and set gid+uid to non-root.
+   Silcd will not run as root, so trying to set either user or group to
+   root will cause silcd to exit. */
+
+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);
+      }
+    }
+  } else {
+    /* Fork server to background, making it a daemon */
+    if (fork()) {
+      SILC_LOG_DEBUG(("Server started as user")); 
+      SILC_LOG_DEBUG(("Forking SILC server to background"));
+      exit(0);
+    }
+    setsid();
+  }
+}
+
 /* 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. */
@@ -344,7 +471,9 @@ void silc_server_stop(SilcServer server)
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
-/* The heart of the server. This runs the scheduler thus runs the server. */
+/* The heart of the server. This runs the scheduler thus runs the server. 
+   When this returns the server has been stopped and the program will
+   be terminated. */
 
 void silc_server_run(SilcServer server)
 {
@@ -355,6 +484,124 @@ void silc_server_run(SilcServer server)
   silc_schedule();
 }
 
+/* Timeout callback that will be called to retry connecting to remote
+   router. This is used by both normal and router server. This will wait
+   before retrying the connecting. The timeout is generated by exponential
+   backoff algorithm. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+{
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+
+  SILC_LOG_INFO(("Retrying connecting to a router"));
+
+  /* Calculate next timeout */
+  if (sconn->retry_count >= 1) {
+    sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+    if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
+      sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
+  } else {
+    sconn->retry_timeout = server->params->retry_interval_min;
+  }
+  sconn->retry_count++;
+  sconn->retry_timeout = sconn->retry_timeout +
+    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
+
+  /* If we've reached max retry count, give up. */
+  if (sconn->retry_count > server->params->retry_count && 
+      server->params->retry_keep_trying == FALSE) {
+    SILC_LOG_ERROR(("Could not connect to router, giving up"));
+    return;
+  }
+
+  /* Wait one before retrying */
+  silc_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;
+
+  SILC_LOG_INFO(("Connecting to the router %s on port %d", 
+                sconn->remote_host, sconn->remote_port));
+
+  /* Connect to remote host */
+  sock = silc_net_create_connection(sconn->remote_port, 
+                                   sconn->remote_host);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Could not connect to router"));
+    silc_task_register(server->timeout_queue, fd, 
+                      silc_server_connect_to_router_retry,
+                      context, 0, 1, SILC_TASK_TIMEOUT, 
+                      SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* 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);
+}
+  
 /* 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
@@ -363,148 +610,59 @@ void silc_server_run(SilcServer server)
 SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
-  int sock;
+  SilcServerConnection sconn;
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
-  /* if we are normal SILC server we need to connect to our cell's
+  /* If we are normal SILC server we need to connect to our cell's
      router. */
   if (server->server_type == SILC_SERVER) {
-    SilcProtocol protocol;
-    SilcServerKEInternalContext *proto_ctx;
+    SILC_LOG_DEBUG(("We are normal server"));
 
     /* Create connection to the router, if configured. */
     if (server->config->routers) {
-      sock = silc_net_create_connection(server->config->routers->port, 
-                                       server->config->routers->host);
-      if (sock < 0) {
-       SILC_LOG_ERROR(("Could not connect to router"));
-       silc_schedule_stop();
-       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 = server->config->routers->host;
-      newsocket->port = server->config->routers->port;
-
-      /* Allocate internal protocol context. This is sent as context
-        to 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 = 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 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 executed. */
-      proto_ctx->timeout_task = 
-       silc_task_register(server->timeout_queue, sock, 
-                          silc_server_timeout_remote,
-                          context, 60, 0,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_LOW);
-
-      /* Register the connection for network input and output. This sets
-        that scheduler will listen for incoming packets for this connection 
-        and sets that outgoing packets may be sent to this connection as 
-        well. However, this doesn't set the scheduler for outgoing traffic,
-        it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-        later when outgoing data is available. */
-      SILC_REGISTER_CONNECTION_FOR_IO(sock);
-      
-      /* Run the protocol */
-      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+      /* Allocate connection object for hold connection specific stuff. */
+      sconn = silc_calloc(1, sizeof(*sconn));
+      sconn->server = server;
+      sconn->remote_host = strdup(server->config->routers->host);
+      sconn->remote_port = server->config->routers->port;
+
+      silc_task_register(server->timeout_queue, fd, 
+                        silc_server_connect_router,
+                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
       return;
     }
   }
-  
-  /* if we are a SILC router we need to establish all of our primary
+
+  /* If we are a SILC router we need to establish all of our primary
      routes. */
   if (server->server_type == SILC_ROUTER) {
-    SilcConfigServerSectionServerConnection *ptr;
+    SilcServerConfigSectionServerConnection *ptr;
+
+    SILC_LOG_DEBUG(("We are router"));
 
     /* Create the connections to all our routes */
     ptr = server->config->routers;
     while (ptr) {
-      SilcProtocol protocol;
-      SilcServerKEInternalContext *proto_ctx;
-
-      /* Create the connection to the remote end */
-      sock = silc_net_create_connection(ptr->port, ptr->host);
-      if (sock < 0) {
-       SILC_LOG_ERROR(("Could not connect to router"));
-       silc_schedule_stop();
-       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 = ptr->host;
-      newsocket->port = ptr->port;
-
-      /* Allocate internal protocol context. This is sent as context
-        to 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 = FALSE;
-      
-      /* Perform key exchange protocol. silc_server_connect_to_router_final
-        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 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 executed. */
-      proto_ctx->timeout_task = 
-       silc_task_register(server->timeout_queue, sock, 
-                          silc_server_timeout_remote,
-                          context, 60, 0,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_LOW);
-
-      /* Register the connection for network input and output. This sets
-        that scheduler will listen for incoming packets for this connection 
-        and sets that outgoing packets may be sent to this connection as 
-        well. However, this doesn't set the scheduler for outgoing traffic,
-        it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-        later when outgoing data is available. */
-      SILC_REGISTER_CONNECTION_FOR_IO(sock);
-      
-      /* Run the protocol */
-      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+      SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
+                     ptr->initiator ? "Initiator" : "Responder",
+                     ptr->host, ptr->port));
+
+      if (ptr->initiator) {
+       /* Allocate connection object for hold connection specific stuff. */
+       sconn = silc_calloc(1, sizeof(*sconn));
+       sconn->server = server;
+       sconn->remote_host = strdup(ptr->host);
+       sconn->remote_port = ptr->port;
+
+       silc_task_register(server->timeout_queue, fd, 
+                          silc_server_connect_router,
+                          (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                          SILC_TASK_PRI_NORMAL);
+      }
 
       if (!ptr->next)
        return;
@@ -518,17 +676,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   /* 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;
-
-  /* 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 (silc_task_register(server->io_queue, fd, 
-                        silc_server_accept_new_connection,
-                        (void *)server, 0, 0, 
-                        SILC_TASK_FD,
-                        SILC_TASK_PRI_NORMAL) == NULL) {
-    silc_schedule_stop();
-    return;
-  }
 }
 
 /* Second part of connecting to router(s). Key exchange protocol has been
@@ -540,64 +687,106 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = NULL;
+  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
+  SilcSocketConnection sock = server->sockets[fd];
   SilcServerConnAuthInternalContext *proto_ctx;
+  SilcServerConfigSectionServerConnection *conn = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
-      silc_buffer_free(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;
+    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->sock = sock = server->sockets[fd];
+  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 */
-  proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
-  if (server->config->routers) {
-    SilcConfigServerSectionServerConnection *conn = NULL;
-
-    /* Check if we find a match from user configured connections */
-    conn = silc_config_server_find_router_conn(server->config,
-                                              sock->hostname,
-                                              sock->port);
-    if (conn) {
-      /* Match found. Use the configured authentication method */
-      proto_ctx->auth_meth = conn->auth_meth;
-      if (conn->auth_data) {
-       proto_ctx->auth_data = strdup(conn->auth_data);
-       proto_ctx->auth_data_len = strlen(conn->auth_data);
-      }
-    } else {
-      /* No match found. */
-      /* XXX */
+  /* Resolve the authentication method used in this connection. Check if 
+     we find a match from user configured connections */
+  conn = silc_server_config_find_router_conn(server->config,
+                                            sock->hostname,
+                                            sock->port);
+  if (conn) {
+    /* Match found. Use the configured authentication method */
+    proto_ctx->auth_meth = conn->auth_meth;
+    if (conn->auth_data) {
+      proto_ctx->auth_data = strdup(conn->auth_data);
+      proto_ctx->auth_data_len = strlen(conn->auth_data);
     }
   } else {
-    /* XXX */
+    SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
+                   sock->hostname, sock->ip, sock->port));
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    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_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
   sock->protocol = NULL;
 
@@ -633,43 +822,35 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcServerConnAuthInternalContext *ctx = 
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
+  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
   SilcSocketConnection sock = ctx->sock;
   SilcServerEntry id_entry;
-  SilcUnknownEntry conn_data;
   SilcBuffer packet;
+  SilcServerHBContext hb_context;
   unsigned char *id_string;
+  SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    silc_protocol_free(protocol);
-    if (ctx->packet)
-      silc_buffer_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;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
-    return;
+    goto out;
   }
 
   /* 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) {
-    if (silc_task_register(server->io_queue, server->sock, 
-                          silc_server_accept_new_connection,
-                          (void *)server, 0, 0, 
-                          SILC_TASK_FD,
-                          SILC_TASK_PRI_NORMAL) == NULL) {
-      silc_schedule_stop();
-      return;
-    } else {
-      server->listenning = TRUE;
-    }
+    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
@@ -696,28 +877,61 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add the connected router to local server list */
   server->standalone = FALSE;
-  conn_data = (SilcUnknownEntry)sock->user_data;
-  id_entry =
-    silc_idlist_add_server(server->local_list, 
-                          sock->hostname ? sock->hostname : sock->ip,
-                          SILC_ROUTER, ctx->dest_id, NULL,
-                          conn_data->send_key, conn_data->receive_key,
-                          conn_data->pkcs, conn_data->hmac, NULL, sock);
-  if (id_entry) {
-    id_entry->hmac_key = conn_data->hmac_key;
-    id_entry->hmac_key_len = conn_data->hmac_key_len;
-    sock->user_data = (void *)id_entry;
-    sock->type = SILC_SOCKET_TYPE_ROUTER;
-    server->id_entry->router = id_entry;
+  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;
+  }
+
+  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);
+
+  /* Announce our clients and channels to the router */
+  silc_server_announce_clients(server);
+  silc_server_announce_channels(server);
+
+ out:
+  /* Free the temporary connection data context */
+  if (sconn) {
+    silc_free(sconn->remote_host);
+    silc_free(sconn);
   }
-    
-  /* Free the temporary connection data context from key exchange */
-  silc_free(conn_data);
 
   /* Free the protocol object */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
   silc_free(ctx);
@@ -732,24 +946,24 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   SilcServer server = (SilcServer)context;
   SilcSocketConnection newsocket;
   SilcServerKEInternalContext *proto_ctx;
-  int sock;
+  int sock, port;
+  void *cconfig, *sconfig, *rconfig;
 
   SILC_LOG_DEBUG(("Accepting new connection"));
 
+  server->stat.conn_attempts++;
+
   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;
   }
 
   /* Check max connections */
   if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    if (server->config->redirect) {
-      /* XXX Redirecting connection to somewhere else now?? */
-      /*silc_server_send_notify("Server is full, trying to redirect..."); */
-    } else {
-      SILC_LOG_ERROR(("Refusing connection, server is full"));
-    }
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
     return;
   }
 
@@ -765,14 +979,60 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   /* 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 mandatory name and address lookups for the remote host. */
-  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if (!newsocket->ip || !newsocket->hostname) {
-    SILC_LOG_DEBUG(("IP lookup/DNS lookup failed"));
-    SILC_LOG_ERROR(("IP lookup/DNS lookup failed"));
+  /* 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);
+
+  /* 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);
+
+  /* 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. */
+  port = server->sockets[fd]->port; /* Listenning port */
+  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;
   }
 
+  /* The connection is allowed */
+
   SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
                 newsocket->ip));
 
@@ -783,11 +1043,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   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);
@@ -802,14 +1066,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
                       context, 60, 0,
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_LOW);
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it
-     will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  SILC_REGISTER_CONNECTION_FOR_IO(sock);
 }
 
 /* Second part of accepting new connection. Key exchange protocol has been
@@ -824,42 +1080,78 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = NULL;
+  SilcSocketConnection sock = server->sockets[fd];
   SilcServerConnAuthInternalContext *proto_ctx;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
-      silc_buffer_free(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: "
                                  "Key exchange failed");
+    server->stat.auth_failures++;
     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");
+    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 = server->sockets[fd];
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;   /* Save SKE object from previous protocol */
   proto_ctx->responder = TRUE;
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
+  proto_ctx->cconfig = ctx->cconfig;
+  proto_ctx->sconfig = ctx->sconfig;
+  proto_ctx->rconfig = ctx->rconfig;
 
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
   sock->protocol = NULL;
 
@@ -893,14 +1185,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
+  SilcServerHBContext hb_context;
+  void *id_entry = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     if (ctx->packet)
-      silc_buffer_free(ctx->packet);
+      silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     if (ctx->dest_id)
@@ -908,8 +1204,11 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     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;
   }
 
@@ -918,7 +1217,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry client;
-      SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
 
       SILC_LOG_DEBUG(("Remote host is client"));
       SILC_LOG_INFO(("Connection from %s (%s) is client", sock->hostname,
@@ -927,29 +1225,29 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       /* 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, conn_data->send_key, 
-                              conn_data->receive_key, conn_data->pkcs,
-                              conn_data->hmac, NULL, sock);
+      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(conn_data);
+       silc_free(sock->user_data);
        break;
       }
 
-      /* Free the temporary connection data context from key exchange */
-      silc_free(conn_data);
+      /* Statistics */
+      server->stat.my_clients++;
+      server->stat.clients++;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_clients++;
 
-      /* Add to sockets internal pointer for fast referencing */
-      sock->user_data = (void *)client;
+      id_entry = (void *)client;
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry new_server;
-      SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
+      SilcServerConfigSectionServerConnection *conn = 
+       sock->type == SILC_SOCKET_TYPE_SERVER ? ctx->sconfig : ctx->rconfig;
 
       SILC_LOG_DEBUG(("Remote host is %s", 
                      sock->type == SILC_SOCKET_TYPE_SERVER ? 
@@ -960,49 +1258,75 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       /* Add the server into server cache. The server name and Server ID
         is updated after we have received NEW_SERVER packet from the
-        server. */
+        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, NULL,
-                              conn_data->send_key, conn_data->receive_key,
-                              conn_data->pkcs, conn_data->hmac, NULL, sock);
+                              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(conn_data);
+       silc_free(sock->user_data);
        break;
       }
-      
-      new_server->registered = TRUE;
-      new_server->hmac_key = conn_data->hmac_key;
-      new_server->hmac_key_len = conn_data->hmac_key_len;
-      
-      /* Free the temporary connection data context from protocols */
-      silc_free(conn_data);
 
-      /* Add to sockets internal pointer for fast referencing */
-      sock->user_data = (void *)new_server;
+      /* Statistics */
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.my_servers++;
+      else
+       server->stat.my_routers++;
+      server->stat.servers++;
 
-      /* There is connection to other server now, if it is router then
-        we will have connection to outside world.  If we are router but
-        normal server connected to us then we will remain standalone,
-        if we are standlone. */
+      id_entry = (void *)new_server;
+
+      /* 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;
+
        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;
   }
 
+  /* 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;
+
   /* 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_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
   if (ctx->dest_id)
@@ -1011,43 +1335,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   sock->protocol = NULL;
 }
 
-/* Internal routine that sends packet or marks packet to be sent. This
-   is used directly only in special cases. Normal cases should use
-   silc_server_packet_send. Returns < 0 error. */
-
-static int silc_server_packet_send_real(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       int force_send)
-{
-  int ret;
-
-  /* Send the packet */
-  ret = silc_packet_send(sock, force_send);
-  if (ret != -2)
-    return ret;
-
-  /* Mark that there is some outgoing data available for this connection. 
-     This call sets the connection both for input and output (the input
-     is set always and this call keeps the input setting, actually). 
-     Actual data sending is performed by silc_server_packet_process. */
-  SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock);
-
-  /* Mark to socket that data is pending in outgoing buffer. This flag
-     is needed if new data is added to the buffer before the earlier
-     put data is sent to the network. */
-  SILC_SET_OUTBUF_PENDING(sock);
-
-  return 0;
-}
-
-typedef struct {
-  SilcPacketContext *packetdata;
-  SilcServer server;
-  SilcSocketConnection sock;
-  SilcCipher cipher;
-  SilcHmac hmac;
-} SilcServerInternalPacket;
-
 /* This function is used to read packets from network and send packets to
    network. This is usually a generic task. */
 
@@ -1055,27 +1342,39 @@ 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;
 
+  if (!sock)
+    return;
+
   SILC_LOG_DEBUG(("Processing packet"));
 
   /* Packet sending */
+
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
+
+    server->stat.packets_sent++;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    ret = silc_server_packet_send_real(server, sock, TRUE);
+    /* Send the packet */
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
-    
+
+    if (ret == -1)
+      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. 
@@ -1088,89 +1387,101 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   }
 
   /* Packet receiving */
-  if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
 
-    /* Read some data from connection */
-    ret = silc_packet_receive(sock);
-    if (ret < 0)
-      return;
+  /* 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;
-      }
+  /* EOF */
+  if (ret == 0) {
+    SILC_LOG_DEBUG(("Read EOF"));
       
-      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
-
+    /* If connection is disconnecting already we will finally
+       close the connection */
+    if (SILC_IS_DISCONNECTING(sock)) {
       if (sock->user_data)
-         silc_server_free_sock_user_data(server, sock);
+       silc_server_free_sock_user_data(server, sock);
       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 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 invalid connection"));
-      return;
-    }
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock);
+    silc_server_close_connection(server, sock);
+    return;
+  }
 
-    switch (sock->type) {
-    case SILC_SOCKET_TYPE_CLIENT:
-      {
-       SilcClientEntry clnt = (SilcClientEntry)sock->user_data;
-       if (!clnt)
-         break;
+  /* 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;
+  }
 
-       clnt->last_receive = time(NULL);
+  server->stat.packets_received++;
 
-       cipher = clnt->receive_key;
-       hmac = clnt->hmac;
-       break;
-      }
-    case SILC_SOCKET_TYPE_SERVER:
-    case SILC_SOCKET_TYPE_ROUTER:
-      {
-       SilcServerEntry srvr = (SilcServerEntry)sock->user_data;
-       if (!srvr)
-         break;
+  /* 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);
+}
 
-       srvr->last_receive = time(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 */
 
-       cipher = srvr->receive_key;
-       hmac = srvr->hmac;
-       break;
-      }
-    case SILC_SOCKET_TYPE_UNKNOWN:
-      {
-       SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
-       if (!conn_data)
-         break;
+static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
+                                           SilcBuffer buffer,
+                                           SilcPacketContext *packet,
+                                           void *context)
+{
+  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
+  SilcServer server = (SilcServer)parse_ctx->context;
 
-       cipher = conn_data->receive_key;
-       hmac = conn_data->hmac;
-       break;
-      }
-    default:
-      return;
-    }
-    /* 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);
-  }
-}
+  /* Packet is normal packet, if: 
 
+     1) packet is private message packet and does not have private key set
+     2) is other packet than channel message packet
+     3) is channel message packet and remote is router and we are router 
+
+     all other packets are special packets 
+  */
+
+  if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
+      (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+    return FALSE;
+
+  if (packet_type != SILC_PACKET_CHANNEL_MESSAGE || 
+      (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
+       parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
+       server->server_type == SILC_ROUTER))
+    return TRUE;
+
+  return FALSE;
+}
+  
 /* Parses whole packet, received earlier. */
 
 SILC_TASK_CALLBACK(silc_server_packet_parse_real)
@@ -1179,14 +1490,16 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   SilcServer server = (SilcServer)parse_ctx->context;
   SilcSocketConnection sock = parse_ctx->sock;
   SilcPacketContext *packet = parse_ctx->packet;
-  SilcBuffer buffer = packet->buffer;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
   int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Decrypt the received packet */
-  ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac, 
-                           buffer, 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;
 
@@ -1201,22 +1514,64 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
 
   if (ret == SILC_PACKET_NONE)
     goto out;
-  
+
+  /* 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 (SILC_ID_CLIENT_COMPARE(client->id, id)) {
+       silc_free(id);
+       goto out;
+      }
+      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 &&
+       SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+      
+      /* 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;
+    }
+  }
+
   /* Parse the incoming packet type */
   silc_server_packet_parse_type(server, sock, packet);
 
+  if (server->server_type == SILC_ROUTER) {
+    /* Broadcast packet if it is marked as broadcast packet and it is
+       originated from router and we are router. */
+    if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+       packet->flags & SILC_PACKET_FLAG_BROADCAST &&
+       !server->standalone) {
+      silc_server_packet_broadcast(server, server->router->connection, packet);
+    }
+  }
+
  out:
-  silc_buffer_clear(sock->inbuf);
-  if (packet->src_id)
-    silc_free(packet->src_id);
-  if (packet->dst_id)
-    silc_free(packet->dst_id);
-  silc_free(packet);
+  /*  silc_buffer_clear(sock->inbuf); */
+  silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
 
 /* Parser callback called by silc_packet_receive_process. This merely
-   registers timeout that will handle the actual parsing whem appropriate. */
+   registers timeout that will handle the actual parsing when appropriate. */
 
 void silc_server_packet_parse(SilcPacketParserContext *parser_context)
 {
@@ -1224,8 +1579,8 @@ void silc_server_packet_parse(SilcPacketParserContext *parser_context)
   SilcSocketConnection sock = parser_context->sock;
 
   switch (sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
   case SILC_SOCKET_TYPE_UNKNOWN:
+  case SILC_SOCKET_TYPE_CLIENT:
     /* Parse the packet with timeout */
     silc_task_register(server->timeout_queue, sock->sock,
                       silc_server_packet_parse_real,
@@ -1254,7 +1609,6 @@ void silc_server_packet_parse_type(SilcServer server,
                                   SilcSocketConnection sock,
                                   SilcPacketContext *packet)
 {
-  SilcBuffer buffer = packet->buffer;
   SilcPacketType type = packet->type;
 
   SILC_LOG_DEBUG(("Parsing packet type %d", type));
@@ -1263,7 +1617,10 @@ void silc_server_packet_parse_type(SilcServer server,
   switch(type) {
   case SILC_PACKET_DISCONNECT:
     SILC_LOG_DEBUG(("Disconnect packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     break;
+
   case SILC_PACKET_SUCCESS:
     /*
      * Success received for something. For now we can have only
@@ -1271,11 +1628,14 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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;
+
   case SILC_PACKET_FAILURE:
     /*
      * Failure received for something. For now we can have only
@@ -1283,28 +1643,52 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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) {
-      /* XXX Audit the failure type */
-      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-      sock->protocol->execute(server->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
+      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;
+
   case SILC_PACKET_REJECT:
     SILC_LOG_DEBUG(("Reject packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     return;
     break;
 
+  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;
+
     /* 
      * Channel packets
      */
   case SILC_PACKET_CHANNEL_MESSAGE:
     /*
      * Received channel message. Channel messages are special packets
-     * (although probably most common ones) hence they are handled
+     * (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;
 
@@ -1316,6 +1700,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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;
 
@@ -1324,21 +1710,25 @@ void silc_server_packet_parse_type(SilcServer server,
      */
   case SILC_PACKET_COMMAND:
     /*
-     * Recived command. Allocate command context and execute the 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;
 
   case SILC_PACKET_COMMAND_REPLY:
     /*
-     * Received command reply packet. Servers never send commands thus
-     * they don't receive command reply packets either, except in cases
-     * where server has forwarded command packet coming from client. 
-     * This must be the case here or we will ignore the packet.
+     * 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"));
-    silc_server_packet_relay_command_reply(server, sock, packet);
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_command_reply(server, sock, packet);
     break;
 
     /*
@@ -1350,10 +1740,18 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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;
 
   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;
 
     /*
@@ -1361,13 +1759,16 @@ void silc_server_packet_parse_type(SilcServer server,
      */
   case SILC_PACKET_KEY_EXCHANGE:
     SILC_LOG_DEBUG(("KE packet"));
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1382,23 +1783,44 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_KEY_EXCHANGE_1:
     SILC_LOG_DEBUG(("KE 1 packet"));
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
-
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-      if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      proto_ctx->packet = buffer;
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      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;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
-                             0, 100000);
+       /* 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."));
@@ -1407,23 +1829,44 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_KEY_EXCHANGE_2:
     SILC_LOG_DEBUG(("KE 2 packet"));
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
-
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-      if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      proto_ctx->packet = buffer;
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      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;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
-                             0, 100000);
+       /* 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 2 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -1431,9 +1874,17 @@ void silc_server_packet_parse_type(SilcServer server,
     break;
 
   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
-    /* If 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. */
+    /*
+     * 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
@@ -1442,13 +1893,16 @@ void silc_server_packet_parse_type(SilcServer server,
     /* Start of the authentication protocol. We receive here the 
        authentication data and will verify it. */
     SILC_LOG_DEBUG(("Connection auth packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
     if (sock->protocol && sock->protocol->protocol->type 
        == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
 
       SilcServerConnAuthInternalContext *proto_ctx = 
        (SilcServerConnAuthInternalContext *)sock->protocol->context;
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1467,7 +1921,10 @@ void silc_server_packet_parse_type(SilcServer server,
      * SILC network.
      */
     SILC_LOG_DEBUG(("New ID packet"));
-    silc_server_new_id(server, sock, 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;
 
   case SILC_PACKET_NEW_CLIENT:
@@ -1477,6 +1934,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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;
 
@@ -1487,774 +1946,530 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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;
 
   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;
 
-  case SILC_PACKET_NEW_CHANNEL_USER:
-    break;
-
-  case SILC_PACKET_NEW_CHANNEL_LIST:
-    break;
-
-  case SILC_PACKET_NEW_CHANNEL_USER_LIST:
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat.
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     break;
 
-  case SILC_PACKET_REPLACE_ID:
+  case SILC_PACKET_KEY_AGREEMENT:
     /*
-     * Received replace ID packet. This sends the old ID that is to be
-     * replaced with the new one included into the packet. Client must not
-     * send this packet.
+     * Received heartbeat.
      */
-    SILC_LOG_DEBUG(("Replace ID packet"));
-    silc_server_replace_id(server, sock, packet);
+    SILC_LOG_DEBUG(("Key agreement packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_key_agreement(server, sock, packet);
     break;
 
-  case SILC_PACKET_REMOVE_ID:
+  case SILC_PACKET_REKEY:
+    /*
+     * Received re-key packet. The sender wants to regenerate the session
+     * keys.
+     */
+    SILC_LOG_DEBUG(("Re-key packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_rekey(server, sock, packet);
     break;
 
-  case SILC_PACKET_REMOVE_CHANNEL_USER:
+  case SILC_PACKET_REKEY_DONE:
     /*
-     * Received packet to remove user from a channel. Routers notify other
-     * routers about a user leaving a channel.
+     * The re-key is done.
      */
-    SILC_LOG_DEBUG(("Remove Channel User packet"));
-    silc_server_remove_channel_user(server, sock, packet);
-    break;
+    SILC_LOG_DEBUG(("Re-key done packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-  default:
-    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
-    break;
-  }
-  
-}
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
 
-/* Assembles a new packet to be sent out to network. This doesn't actually
-   send the packet but creates the packet and fills the outgoing data
-   buffer and marks the packet ready to be sent to network. However, If 
-   argument force_send is TRUE the packet is sent immediately and not put 
-   to queue. Normal case is that the packet is not sent immediately. */
-
-void silc_server_packet_send(SilcServer server,
-                            SilcSocketConnection sock, 
-                            SilcPacketType type, 
-                            SilcPacketFlags flags,
-                            unsigned char *data, 
-                            unsigned int data_len,
-                            int force_send)
-{
-  void *dst_id = NULL;
-  SilcIdType dst_id_type = SILC_ID_NONE;
+      SilcServerRekeyInternalContext *proto_ctx = 
+       (SilcServerRekeyInternalContext *)sock->protocol->context;
 
-  if (!sock)
-    return;
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (((SilcClientEntry)sock->user_data)->id) {
-      dst_id = ((SilcClientEntry)sock->user_data)->id;
-      dst_id_type = SILC_ID_CLIENT;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (((SilcServerEntry)sock->user_data)->id) {
-      dst_id = ((SilcServerEntry)sock->user_data)->id;
-      dst_id_type = SILC_ID_SERVER;
+      proto_ctx->packet = silc_packet_context_dup(packet);
+
+      /* Let the protocol handle the packet */
+      sock->protocol->execute(server->timeout_queue, 0, 
+                             sock->protocol, sock->sock, 0, 0);
+    } else {
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
     }
     break;
+
   default:
+    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
   }
+  
+}
 
-  silc_server_packet_send_dest(server, sock, type, flags, dst_id,
-                              dst_id_type, data, data_len, force_send);
+/* Creates connection to a remote router. */
+
+void silc_server_create_connection(SilcServer server,
+                                  char *remote_host, uint32 port)
+{
+  SilcServerConnection sconn;
+
+  /* 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;
+
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_connect_router,
+                    (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                    SILC_TASK_PRI_NORMAL);
 }
 
-/* Assembles a new packet to be sent out to network. This doesn't actually
-   send the packet but creates the packet and fills the outgoing data
-   buffer and marks the packet ready to be sent to network. However, If 
-   argument force_send is TRUE the packet is sent immediately and not put 
-   to queue. Normal case is that the packet is not sent immediately. 
-   Destination information is sent as argument for this function. */
-
-void silc_server_packet_send_dest(SilcServer server,
-                                 SilcSocketConnection sock, 
-                                 SilcPacketType type, 
-                                 SilcPacketFlags flags,
-                                 void *dst_id,
-                                 SilcIdType dst_id_type,
-                                 unsigned char *data, 
-                                 unsigned int data_len,
-                                 int force_send)
+SILC_TASK_CALLBACK(silc_server_close_connection_final)
 {
-  SilcPacketContext packetdata;
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
-  unsigned char *dst_id_data = NULL;
-  unsigned int dst_id_len = 0;
+  silc_socket_free((SilcSocketConnection)context);
+}
 
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+/* Closes connection to socket connection */
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      cipher = ((SilcClientEntry)sock->user_data)->send_key;
-      hmac = ((SilcClientEntry)sock->user_data)->hmac;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      cipher = ((SilcServerEntry)sock->user_data)->send_key;
-      hmac = ((SilcServerEntry)sock->user_data)->hmac;
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      /* We don't know what type of connection this is thus it must
-        be in authentication phase. */
-      cipher = ((SilcUnknownEntry)sock->user_data)->send_key;
-      hmac = ((SilcUnknownEntry)sock->user_data)->hmac;
-    }
-    break;
-  }
+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));
 
-  if (dst_id) {
-    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
-    dst_id_len = silc_id_get_len(dst_id_type);
-  }
+  /* We won't listen for this connection anymore */
+  silc_schedule_unset_listen_fd(sock->sock);
 
-  /* Set the packet context pointers */
-  packetdata.type = type;
-  packetdata.flags = flags;
-  packetdata.src_id = silc_id_id2str(server->id, server->id_type);
-  packetdata.src_id_len = SILC_ID_SERVER_LEN;
-  packetdata.src_id_type = server->id_type;
-  packetdata.dst_id = dst_id_data;
-  packetdata.dst_id_len = dst_id_len;
-  packetdata.dst_id_type = dst_id_type;
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-  packetdata.rng = server->rng;
+  /* Unregister all tasks */
+  silc_task_unregister_by_fd(server->io_queue, sock->sock);
+  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
 
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          data_len);
+  /* Close the actual connection */
+  silc_net_close_connection(sock->sock);
+  server->sockets[sock->sock] = NULL;
 
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_close_connection_final,
+                    (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
+                    SILC_TASK_PRI_NORMAL);
+}
 
-  packetdata.buffer = sock->outbuf;
+/* Sends disconnect message to remote connection and disconnects the 
+   connection. */
 
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+void silc_server_disconnect_remote(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  const char *fmt, ...)
+{
+  va_list ap;
+  unsigned char buf[4096];
 
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
+  if (!sock)
+    return;
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+  memset(buf, 0, sizeof(buf));
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  va_end(ap);
 
-  SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  SILC_LOG_DEBUG(("Disconnecting remote host"));
 
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
+  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")));
 
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
-}
+  /* 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);
 
-/* Forwards packet. Packets sent with this function will be marked as
-   forwarded (in the SILC header flags) so that the receiver knows that
-   we have forwarded the packet to it. Forwarded packets are handled
-   specially by the receiver as they are not destined to the receiver
-   originally. However, the receiver knows this because the forwarded
-   flag has been set (and the flag is authenticated). */
-
-void silc_server_packet_forward(SilcServer server,
-                               SilcSocketConnection sock,
-                               unsigned char *data, unsigned int data_len,
-                               int force_send)
-{
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
+  /* Mark the connection to be disconnected */
+  SILC_SET_DISCONNECTED(sock);
+  silc_server_close_connection(server, sock);
+}
 
-  SILC_LOG_DEBUG(("Forwarding packet"));
+typedef struct {
+  SilcServer server;
+  SilcClientEntry client;
+} *FreeClientInternal;
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      cipher = ((SilcClientEntry )sock->user_data)->send_key;
-      hmac = ((SilcClientEntry )sock->user_data)->hmac;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      cipher = ((SilcServerEntry )sock->user_data)->send_key;
-      hmac = ((SilcServerEntry )sock->user_data)->hmac;
-    }
-    break;
-  default:
-    /* We won't forward to unknown destination - keys must exist with
-       the destination before forwarding. */
-    return;
-  }
+SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
+{
+  FreeClientInternal i = (FreeClientInternal)context;
 
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 0, 0, data_len);
+  silc_idlist_del_data(i->client);
+  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
+  silc_free(i);
+}
 
-  /* Mungle the packet flags and add the FORWARDED flag */
-  if (data)
-    data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED;
+/* Frees client data and notifies about client's signoff. */
 
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+void silc_server_free_client_data(SilcServer server, 
+                                 SilcSocketConnection sock,
+                                 SilcClientEntry client, 
+                                 int notify,
+                                 char *signoff)
+{
+  FreeClientInternal i = silc_calloc(1, sizeof(*i));
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+  /* 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_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+    if (sock->outbuf->data - sock->outbuf->head)
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
-}
+    silc_packet_send(sock, TRUE);
 
-/* Internal routine to actually create the channel packet and send it
-   to network. This is common function in channel message sending. If
-   `channel_message' is TRUE this encrypts the message as it is strictly
-   a channel message. If FALSE normal encryption process is used. */
+    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
 
-static void
-silc_server_packet_send_to_channel_real(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       SilcPacketContext *packet,
-                                       SilcCipher cipher,
-                                       SilcHmac hmac,
-                                       unsigned char *data,
-                                       unsigned int data_len,
-                                       int channel_message,
-                                       int force_send)
-{
-  packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
-    packet->src_id_len + packet->dst_id_len;
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packet->src_id_len + 
-                          packet->dst_id_len,
-                          packet->padlen,
-                          data_len);
-
-  packet->buffer = sock->outbuf;
-
-  /* Put the data to buffer, assemble and encrypt the packet. The packet
-     is encrypted with normal session key shared with the client. */
-  silc_buffer_put(sock->outbuf, data, data_len);
-  silc_packet_assemble(packet);
-  if (channel_message)
-    silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                       packet->src_id_len + packet->dst_id_len +
-                       packet->padlen);
+  /* Send SIGNOFF notify to routers. */
+  if (notify && !server->standalone && server->router)
+    silc_server_send_notify_signoff(server, server->router->connection,
+                                   server->server_type == SILC_SERVER ?
+                                   FALSE : TRUE, client->id, 
+                                   SILC_ID_CLIENT_LEN, signoff);
+
+  /* Remove client from all channels */
+  if (notify)
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    TRUE, signoff, TRUE);
   else
-    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
-    
-  SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    FALSE, NULL, FALSE);
+
+  /* We will not delete the client entry right away. We will take it
+     into history (for WHOWAS command) for 5 minutes */
+  i->server = server;
+  i->client = client;
+  silc_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 */
+  server->stat.my_clients--;
+  server->stat.clients--;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_clients--;
 }
 
-/* This routine is used by the server to send packets to channel. The 
-   packet sent with this function is distributed to all clients on
-   the channel. Usually this is used to send notify messages to the
-   channel, things like notify about new user joining to the channel. */
+/* 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_packet_send_to_channel(SilcServer server,
-                                       SilcChannelEntry channel,
-                                       SilcPacketType type,
-                                       unsigned char *data,
-                                       unsigned int data_len,
-                                       int force_send)
+void silc_server_free_sock_user_data(SilcServer server, 
+                                    SilcSocketConnection sock)
 {
-  SilcSocketConnection sock = NULL;
-  SilcPacketContext packetdata;
-  SilcClientEntry client = NULL;
-  SilcServerEntry *routed = NULL;
-  SilcChannelClientEntry chl;
-  unsigned int routed_count = 0;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  /* This doesn't send channel message packets */
-  if (type == SILC_PACKET_CHANNEL_MESSAGE)
-    return;
-  
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = type;
-  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
-  packetdata.src_id_len = SILC_ID_SERVER_LEN;
-  packetdata.src_id_type = SILC_ID_SERVER;
-  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-
-  /* If there are global users in the channel we will send the message
-     first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
-      channel->global_users) {
-    SilcServerEntry router;
-
-    /* Get data used in packet header encryption, keys and stuff. */
-    router = server->id_entry->router;
-    sock = (SilcSocketConnection)router->connection;
-    cipher = router->send_key;
-    hmac = router->hmac;
-    
-    SILC_LOG_DEBUG(("Sending channel message to router for routing"));
-
-    silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                           cipher, hmac, data,
-                                           data_len, FALSE, force_send);
-  }
-
-  /* Send the message to clients on the channel's client list. */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    client = chl->client;
-
-    /* If client has router set it is not locally connected client and
-       we will route the message to the router set in the client. */
-    if (client && client->router && server->server_type == SILC_ROUTER) {
-      int k;
-
-      /* Check if we have sent the packet to this route already */
-      for (k = 0; k < routed_count; k++)
-       if (routed[k] == client->router)
-         break;
-      if (k < routed_count)
-       continue;
-
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->router->connection;
-      cipher = client->router->send_key;
-      hmac = client->router->hmac;
-
-      /* Send the packet */
-      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                             cipher, hmac, data,
-                                             data_len, FALSE, force_send);
-
-      /* We want to make sure that the packet is routed to same router
-        only once. Mark this route as sent route. */
-      k = routed_count;
-      routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
-      routed[k] = client->router;
-      routed_count++;
+  SILC_LOG_DEBUG(("Start"));
 
-      continue;
+  switch(sock->type) {
+  case SILC_SOCKET_TYPE_CLIENT:
+    {
+      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
+      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
+      break;
     }
+  case SILC_SOCKET_TYPE_SERVER:
+  case SILC_SOCKET_TYPE_ROUTER:
+    {
+      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+
+      /* Free all client entries that this server owns as they will
+        become invalid now as well. */
+      if (user_data->id)
+       silc_server_remove_clients_by_server(server, user_data, TRUE);
+
+      /* If this was our primary router connection then we're lost to
+        the outside world. */
+      if (server->router == user_data) {
+       server->id_entry->router = NULL;
+       server->router = NULL;
+       server->standalone = TRUE;
+      }
 
-    /* Send to locally connected client */
-    if (client) {
+      /* 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;
 
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->connection;
-      cipher = client->send_key;
-      hmac = client->hmac;
-      
-      /* Send the packet */
-      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                             cipher, hmac, data,
-                                             data_len, FALSE, force_send);
+      silc_idlist_del_data(user_data);
+      silc_free(user_data);
+      break;
     }
   }
 
-  if (routed_count)
-    silc_free(routed);
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
+  sock->user_data = NULL;
 }
 
-/* This routine is explicitly used to relay messages to some channel.
-   Packets sent with this function we have received earlier and are
-   totally encrypted. This just sends the packet to all clients on
-   the channel. If the sender of the packet is someone on the channel 
-   the message will not be sent to that client. The SILC Packet header
-   is encrypted with the session key shared between us and the client.
-   MAC is also computed before encrypting the header. Rest of the
-   packet will be untouched. */
-
-void silc_server_packet_relay_to_channel(SilcServer server,
-                                        SilcSocketConnection sender_sock,
-                                        SilcChannelEntry channel,
-                                        void *sender, 
-                                        SilcIdType sender_type,
-                                        unsigned char *data,
-                                        unsigned int data_len,
-                                        int force_send)
+/* 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. */
+
+int silc_server_remove_clients_by_server(SilcServer server, 
+                                        SilcServerEntry entry,
+                                        int server_signoff)
 {
-  int found = FALSE;
-  SilcSocketConnection sock = NULL;
-  SilcPacketContext packetdata;
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
   SilcClientEntry client = NULL;
-  SilcServerEntry *routed = NULL;
-  SilcChannelClientEntry chl;
-  unsigned int routed_count = 0;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  SILC_LOG_DEBUG(("Relaying packet to channel"));
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = silc_id_id2str(sender, sender_type);
-  packetdata.src_id_len = silc_id_get_len(sender_type);
-  packetdata.src_id_type = sender_type;
-  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* If there are global users in the channel we will send the message
-     first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
-      channel->global_users) {
-    SilcServerEntry router;
-
-    router = server->id_entry->router;
-
-    /* Check that the sender is not our router. */
-    if (sender_sock != (SilcSocketConnection)router->connection) {
-
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)router->connection;
-      cipher = router->send_key;
-      hmac = router->hmac;
-
-      SILC_LOG_DEBUG(("Sending channel message to router for routing"));
-
-      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                             cipher, hmac, data,
-                                             data_len, TRUE, force_send);
-    }
-  }
-
-  /* Send the message to clients on the channel's client list. */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    client = chl->client;
+  SilcBuffer idp;
+  SilcClientEntry *clients = NULL;
+  uint32 clients_c = 0;
+  unsigned char **argv = NULL;
+  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  int i;
 
-    if (client) {
+  SILC_LOG_DEBUG(("Start"));
 
-      /* If sender is one on the channel do not send it the packet. */
-      if (!found && !SILC_ID_CLIENT_COMPARE(client->id, sender)) {
-       found = TRUE;
-       continue;
-      }
+  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 the client has set router it means that it is not locally
-        connected client and we will route the packet further. */
-      if (server->server_type == SILC_ROUTER && client->router) {
-       int k;
+  if (silc_idcache_find_by_id(server->local_list->clients, 
+                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
 
-       /* Sender maybe server as well so we want to make sure that
-          we won't send the message to the server it came from. */
-       if (!found && !SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
-         found = TRUE;
-         continue;
+    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;
        }
 
-       /* Check if we have sent the packet to this route already */
-       for (k = 0; k < routed_count; k++)
-         if (routed[k] == client->router)
-           break;
-       if (k < routed_count)
-         continue;
-       
-       /* Get data used in packet header encryption, keys and stuff. */
-       sock = (SilcSocketConnection)client->router->connection;
-       cipher = client->router->send_key;
-       hmac = client->router->hmac;
-
-       /* Send the packet */
-       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                               cipher, hmac, data,
-                                               data_len, TRUE, force_send);
-       
-       /* We want to make sure that the packet is routed to same router
-          only once. Mark this route as sent route. */
-       k = routed_count;
-       routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
-       routed[k] = client->router;
-       routed_count++;
-       
-       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++;
+         }
 
-      /* XXX Check client's mode on the channel. */
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
 
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->connection;
-      cipher = client->send_key;
-      hmac = client->hmac;
+       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);
+       }
 
-      SILC_LOG_DEBUG(("Sending packet to client %s", 
-                     sock->hostname ? sock->hostname : sock->ip));
+       /* Remove the client entry */
+       silc_server_remove_from_channels(server, NULL, client, FALSE, 
+                                        NULL, FALSE);
+       silc_idlist_del_client(server->local_list, client);
 
-      /* Send the packet */
-      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                             cipher, hmac, data,
-                                             data_len, TRUE, force_send);
+       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 (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (client->data.registered == FALSE) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+       
+       if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
 
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
-}
-
-/* This function is used to send packets strictly to all local clients
-   on a particular channel.  This is used for example to distribute new
-   channel key to all our locally connected clients on the channel. 
-   The packets are always encrypted with the session key shared between
-   the client, this means these are not _to the channel_ but _to the client_
-   on the channel. */
-
-void silc_server_packet_send_local_channel(SilcServer server,
-                                          SilcChannelEntry channel,
-                                          SilcPacketType type,
-                                          SilcPacketFlags flags,
-                                          unsigned char *data,
-                                          unsigned int data_len,
-                                          int force_send)
-{
-  SilcClientEntry client;
-  SilcChannelClientEntry chl;
-  SilcSocketConnection sock = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Send the message to clients on the channel's client list. */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    client = chl->client;
+       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 (client) {
-      sock = (SilcSocketConnection)client->connection;
+       /* Remove the client entry */
+       silc_server_remove_from_channels(server, NULL, client, FALSE,
+                                        NULL, FALSE);
+       silc_idlist_del_client(server->global_list, client);
 
-      /* Send the packet to the client */
-      silc_server_packet_send_dest(server, sock, type, flags, client->id,
-                                  SILC_ID_CLIENT, data, data_len,
-                                  force_send);
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
     }
+    silc_idcache_list_free(list);
   }
-}
-
-/* Relays received command reply packet to the correct destination. The
-   destination must be one of our locally connected client or the packet
-   will be ignored. This is called when server has forwarded one of
-   client's command request to router and router has now replied to the 
-   command. */
-
-void silc_server_packet_relay_command_reply(SilcServer server,
-                                           SilcSocketConnection sock,
-                                           SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client;
-  SilcClientID *id;
-  SilcSocketConnection dst_sock;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Source must be server or router */
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    goto out;
-
-  /* Destination must be client */
-  if (packet->dst_id_type != SILC_ID_CLIENT)
-    goto out;
 
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
+  /* Send the SERVER_SIGNOFF notify */
+  if (server_signoff) {
+    SilcBuffer args;
+
+    /* 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);
+    }
 
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+    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);
+    }
 
-  /* Destination must be one of ours */
-  client = silc_idlist_find_client_by_id(server->local_list, id);
-  if (!client) {
-    silc_free(id);
-    goto out;
+    silc_free(clients);
+    silc_buffer_free(args);
+    silc_free(argv);
+    silc_free(argv_lens);
+    silc_free(argv_types);
   }
 
-  /* Relay the packet to the client */
-
-  dst_sock = (SilcSocketConnection)client->connection;
-  silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                  + packet->dst_id_len + packet->padlen);
-
-  silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-  silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-
-  /* Encrypt packet */
-  if (client && client->send_key)
-    silc_packet_encrypt(client->send_key, client->hmac, 
-                       dst_sock->outbuf, buffer->len);
-    
-  /* Send the packet */
-  silc_server_packet_send_real(server, dst_sock, FALSE);
-
-  silc_free(id);
-
- out:
-  silc_buffer_free(buffer);
-}
-
-/* Closes connection to socket connection */
-
-void silc_server_close_connection(SilcServer server,
-                                 SilcSocketConnection sock)
-{
-
-  SILC_LOG_DEBUG(("Closing connection %d", 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_socket_free(sock);
+  return TRUE;
 }
 
-/* Sends disconnect message to remote connection and disconnects the 
-   connection. */
+/* 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. */
 
-void silc_server_disconnect_remote(SilcServer server,
-                                  SilcSocketConnection sock,
-                                  const char *fmt, ...)
+int silc_server_channel_has_global(SilcChannelEntry channel)
 {
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  SILC_LOG_DEBUG(("Disconnecting remote host"));
+  SilcChannelClientEntry chl;
 
-  /* 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);
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (chl->client->router)
+      return TRUE;
+  }
 
-  /* Mark the connection to be disconnected */
-  SILC_SET_DISCONNECTED(sock);
-  silc_server_close_connection(server, sock);
+  return FALSE;
 }
 
-/* Free's user_data pointer from socket connection object. As this 
-   pointer maybe anything we wil switch here to find the correct
-   data type and free it the way it needs to be free'd. */
+/* Checks whether given channel has locally connected users.  If it does this
+   returns TRUE and FALSE if there is not one locally connected client. */
 
-void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock)
+int silc_server_channel_has_local(SilcChannelEntry channel)
 {
-  SILC_LOG_DEBUG(("Start"));
-
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    {
-      SilcClientEntry user_data = (SilcClientEntry )sock->user_data;
-
-      /* Remove client from all channels */
-      silc_server_remove_from_channels(server, sock, user_data);
-
-      /* Free the client entry and everything in it */
-      /* XXX must take some info to history before freeing */
-      silc_idlist_del_client(server->local_list, user_data);
-      break;
-    }
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    {
-
-      break;
-    }
-    break;
-  default:
-    {
-      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
+  SilcChannelClientEntry chl;
 
-      if (user_data->send_key)
-       silc_cipher_free(user_data->send_key);
-      if (user_data->receive_key)
-       silc_cipher_free(user_data->receive_key);
-      if (user_data->pkcs)
-       silc_pkcs_free(user_data->pkcs);
-      if (user_data->hmac) {
-       silc_hmac_free(user_data->hmac);
-       memset(user_data->hmac_key, 0, user_data->hmac_key_len);
-       silc_free(user_data->hmac_key);
-      }
-      silc_free(user_data);
-      break;
-    }
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (!chl->client->router)
+      return TRUE;
   }
 
-  sock->user_data = NULL;
-#undef LCC
-#undef LCCC
+  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. */
+/* 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,
-                                     SilcClientEntry client)
+                                     SilcClientEntry client,
+                                     int notify,
+                                     char *signoff_message,
+                                     int keygen)
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
-  SilcBuffer chidp, clidp;
+  SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2268,38 +2483,106 @@ void silc_server_remove_from_channels(SilcServer server,
   silc_list_start(client->channels);
   while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
     channel = chl->channel;
-    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 
-    /* Remove from list */
+    /* Remove channel from client's channel list */
     silc_list_del(client->channels, chl);
 
-    /* If this client is last one on the channel the channel
-       is removed all together. */
-    if (silc_list_count(channel->user_list) < 2) {
-
-      /* However, if the channel has marked global users then the 
-        channel is not created locally, and this does not remove the
-        channel globally from SILC network, in this case we will
-        notify that this client has left the channel. */
-      if (channel->global_users)
-       silc_server_send_notify_to_channel(server, channel,
-                                          SILC_NOTIFY_TYPE_SIGNOFF, 1,
-                                          clidp->data, clidp->len);
-      
-      silc_idlist_del_channel(server->local_list, channel);
+    /* 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);
       continue;
     }
 
-    /* Remove from list */
+    /* Remove client from channel's client list */
     silc_list_del(channel->user_list, chl);
     silc_free(chl);
+    server->stat.my_chanclients--;
+
+    /* If there is no global users on the channel anymore mark the channel
+       as local channel. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_global(channel))
+      channel->global_users = FALSE;
+
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_local(channel)) {
+      /* Notify about leaving client if this channel has global users. */
+      if (notify && channel->global_users)
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                          SILC_NOTIFY_TYPE_SIGNOFF, 
+                                          signoff_message ? 2 : 1,
+                                          clidp->data, clidp->len,
+                                          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--;
+      continue;
+    }
 
     /* Send notify to channel about client leaving SILC and thus
        the entire channel. */
-    silc_server_send_notify_to_channel(server, channel,
-                                      SILC_NOTIFY_TYPE_SIGNOFF, 1,
-                                      clidp->data, clidp->len);
-    silc_buffer_free(chidp);
+    if (notify)
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                        SILC_NOTIFY_TYPE_SIGNOFF, 
+                                        signoff_message ? 2 : 1,
+                                        clidp->data, clidp->len,
+                                        signoff_message, signoff_message ?
+                                        strlen(signoff_message) : 0);
+
+    if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, 0);
+      
+      /* 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 ? 
+                                  FALSE : !server->standalone);
+    }
   }
 
   silc_buffer_free(clidp);
@@ -2334,31 +2617,71 @@ int silc_server_remove_from_one_channel(SilcServer server,
 
     ch = chl->channel;
 
-    /* Remove from list */
+    /* Remove channel from client's channel list */
     silc_list_del(client->channels, chl);
 
-    /* If this client is last one on the channel the channel
-       is removed all together. */
-    if (silc_list_count(channel->user_list) < 2) {
-      /* Notify about leaving client if this channel has global users,
-        ie. the channel is not created locally. */
-      if (notify && channel->global_users)
-       silc_server_send_notify_to_channel(server, channel,
-                                          SILC_NOTIFY_TYPE_LEAVE, 1,
-                                          clidp->data, clidp->len);
-      
-      silc_idlist_del_channel(server->local_list, channel);
+    /* 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;
     }
 
-    /* Remove from list */
+    /* Remove client from channel's client list */
     silc_list_del(channel->user_list, chl);
     silc_free(chl);
+    server->stat.my_chanclients--;
+
+    /* If there is no global users on the channel anymore mark the channel
+       as local channel. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_global(channel))
+      channel->global_users = FALSE;
+
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_local(channel)) {
+      /* Notify about leaving client if this channel has global users. */
+      if (notify && channel->global_users)
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                          SILC_NOTIFY_TYPE_LEAVE, 1,
+                                          clidp->data, clidp->len);
+
+      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, channel,
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                         SILC_NOTIFY_TYPE_LEAVE, 1,
                                         clidp->data, clidp->len);
     break;
@@ -2398,276 +2721,331 @@ 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);
+
   silc_server_disconnect_remote(server, sock, 
                                "Server closed connection: "
                                "Connection timeout");
 }
 
-/* Internal routine used to send (relay, route) private messages to some
-   destination. If the private message key does not exist then the message
-   is re-encrypted, otherwise we just pass it along. */
-
-static void 
-silc_server_private_message_send_internal(SilcServer server,
-                                         SilcSocketConnection dst_sock,
-                                         SilcCipher cipher,
-                                         SilcHmac hmac,
-                                         SilcPacketContext *packet)
+/* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
+   function may be used only by router. In real SILC network all channels
+   are created by routers thus this function is never used by normal
+   server. */
+
+SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
+                                               SilcServerID *router_id,
+                                               char *cipher, 
+                                               char *hmac,
+                                               char *channel_name,
+                                               int broadcast)
 {
-  SilcBuffer buffer = packet->buffer;
-
-  /* Send and re-encrypt if private messge key does not exist */
-  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
-
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    
-    /* Re-encrypt packet */
-    silc_packet_encrypt(cipher, hmac, dst_sock->outbuf, buffer->len);
-    
-    /* Send the packet */
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-
-  } else {
-    /* Key exist so just send it */
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-  }
-}
+  SilcChannelID *channel_id;
+  SilcChannelEntry entry;
+  SilcCipher key;
+  SilcHmac newhmac;
 
-/* Received private message. This resolves the destination of the message 
-   and sends the packet. This is used by both server and router.  If the
-   destination is our locally connected client this sends the packet to
-   the client. This may also send the message for further routing if
-   the destination is not in our server (or router). */
+  SILC_LOG_DEBUG(("Creating new channel"));
 
-void silc_server_private_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcClientID *id;
-  SilcServerEntry router;
-  SilcSocketConnection dst_sock;
-  SilcClientEntry client;
+  if (!cipher)
+    cipher = "aes-256-cbc";
+  if (!hmac)
+    hmac = "hmac-sha1-96";
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Allocate cipher */
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
 
-  if (!packet->dst_id) {
-    SILC_LOG_ERROR(("Bad Client ID in private message packet, dropped"));
-    goto err;
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
   }
 
-  /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
-  if (!id) {
-    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
-    goto err;
-  }
+  channel_name = strdup(channel_name);
 
-  /* If the destination belongs to our server we don't have to route
-     the message anywhere but to send it to the local destination. */
-  client = silc_idlist_find_client_by_id(server->local_list, id);
-  if (client) {
-    /* It exists, now deliver the message to the destination */
-    dst_sock = (SilcSocketConnection)client->connection;
+  /* 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);
+  if (!entry) {
+    silc_free(channel_name);
+    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. */
-      router = (SilcServerEntry)dst_sock->user_data;
-      //      assert(client->router == server->id_entry);
+  entry->cipher = strdup(cipher);
+  entry->hmac_name = strdup(hmac);
 
-      silc_server_private_message_send_internal(server, dst_sock,
-                                               router->send_key,
-                                               router->hmac,
-                                               packet);
-      goto out;
-    }
+  /* Now create the actual key material */
+  silc_server_create_channel_key(server, entry, 
+                                silc_cipher_get_key_len(key) / 8);
 
-    /* Seems that client really is directly connected to us */
-    silc_server_private_message_send_internal(server, dst_sock, 
-                                             client->send_key,
-                                             client->hmac, packet);
-    goto out;
+  /* Notify other routers about the new channel. We send the packet
+     to our primary route. */
+  if (broadcast && server->standalone == FALSE)
+    silc_server_send_new_channel(server, server->router->connection, TRUE, 
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
+
+  server->stat.my_channels++;
+
+  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, 
+                                      char *hmac,
+                                      char *channel_name,
+                                      SilcChannelID *channel_id,
+                                      int broadcast)
+{
+  SilcChannelEntry entry;
+  SilcCipher key;
+  SilcHmac newhmac;
+
+  SILC_LOG_DEBUG(("Creating new channel"));
+
+  if (!cipher)
+    cipher = "aes-256-cbc";
+  if (!hmac)
+    hmac = "hmac-sha1-96";
+
+  /* Allocate cipher */
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
+
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
   }
 
-  /* 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) {
-    router = server->id_entry->router;
-
-    /* Send to primary route */
-    if (router) {
-      dst_sock = (SilcSocketConnection)router->connection;
-      silc_server_private_message_send_internal(server, dst_sock, 
-                                               router->send_key,
-                                               router->hmac, packet);
-    }
-    goto out;
+  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);
+  if (!entry) {
+    silc_free(channel_name);
+    return NULL;
   }
 
-  /* We are router and we will perform route lookup for the destination 
-     and send the message to fastest route. */
-  if (server->server_type == SILC_ROUTER && !server->standalone) {
-    dst_sock = silc_server_get_route(server, id, SILC_ID_CLIENT);
-    router = (SilcServerEntry)dst_sock->user_data;
+  /* Now create the actual key material */
+  silc_server_create_channel_key(server, entry, 
+                                silc_cipher_get_key_len(key) / 8);
 
-    /* Get fastest route and send packet. */
-    if (router)
-      silc_server_private_message_send_internal(server, dst_sock, 
-                                               router->send_key,
-                                               router->hmac, packet);
+  /* Notify other routers about the new channel. We send the packet
+     to our primary route. */
+  if (broadcast && server->standalone == FALSE)
+    silc_server_send_new_channel(server, server->router->connection, TRUE, 
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
 
-    goto out;
-  }
+  server->stat.my_channels++;
 
- err:
-  silc_server_send_error(server, sock, 
-                        "No such nickname: Private message not sent");
- out:
-  silc_buffer_free(buffer);
+  return entry;
 }
 
-/* Process received channel message. The message can be originated from
-   client or server. */
+/* Channel's key re-key timeout callback. */
 
-void silc_server_channel_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
 {
-  SilcChannelEntry channel = NULL;
-  SilcChannelClientEntry chl;
-  SilcChannelID *id = NULL;
-  void *sender = NULL;
-  SilcBuffer buffer = packet->buffer;
+  SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
+  SilcServer server = (SilcServer)rekey->context;
 
-  SILC_LOG_DEBUG(("Processing channel message"));
+  silc_server_create_channel_key(server, rekey->channel, rekey->key_len);
+  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
 
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Received bad message for channel, dropped"));
-    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
-    goto out;
-  }
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_channel_key_rekey,
+                    (void *)rekey, 3600, 0,
+                    SILC_TASK_TIMEOUT,
+                    SILC_TASK_PRI_NORMAL);
+}
 
-  /* Find channel entry */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
-  if (!channel) {
-    SILC_LOG_DEBUG(("Could not find channel"));
-    goto out;
+/* 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)
+{
+  int i;
+  unsigned char channel_key[32], hash[32];
+  uint32 len;
+
+  SILC_LOG_DEBUG(("Generating channel key"));
+
+  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
+    return;
   }
 
-  /* See that this client is on the channel. If the message is coming
-     from router we won't do the check as the message is from client that
-     we don't know about. Also, if the original sender is not client
-     (as it can be server as well) we don't do the check. */
-  sender = silc_id_str2id(packet->src_id, packet->src_id_type);
-  if (sock->type != SILC_SOCKET_TYPE_ROUTER && 
-      packet->src_id_type == SILC_ID_CLIENT) {
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-      if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
-       break;
-    }
-    if (chl == SILC_LIST_END)
-      goto out;
+  if (!channel->channel_key)
+    if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
+      return;
+
+  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;
+
+  /* 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);
+
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
   }
 
-  /* Distribute the packet to our local clients. This will send the
-     packet for further routing as well, if needed. */
-  silc_server_packet_relay_to_channel(server, sock, channel, sender,
-                                     packet->src_id_type,
-                                     packet->buffer->data,
-                                     packet->buffer->len, FALSE);
+  /* Save the key */
+  channel->key_len = len * 8;
+  channel->key = silc_calloc(len, sizeof(*channel->key));
+  memcpy(channel->key, channel_key, len);
+  memset(channel_key, 0, sizeof(channel_key));
 
- out:
-  if (sender)
-    silc_free(sender);
-  if (id)
-    silc_free(id);
-  silc_buffer_free(buffer);
+  /* 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));
+  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);
+  }
 }
 
-/* Received channel key packet. We distribute the key to all of our locally
-   connected clients on the channel. */
-/* XXX Router must accept this packet and distribute the key to all its
-   server that has clients on the channel */
+/* 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. */
 
-void silc_server_channel_key(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel)
 {
-  SilcBuffer buffer = packet->buffer;
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcClientEntry client;
-  unsigned char *tmp;
-  unsigned int tmp_len;
+  unsigned char *tmp, hash[32];
+  uint32 tmp_len;
   char *cipher;
 
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    goto out;
+  SILC_LOG_DEBUG(("Start"));
 
   /* Decode channel key payload */
-  payload = silc_channel_key_payload_parse(buffer);
+  payload = silc_channel_key_payload_parse(key_payload);
   if (!payload) {
     SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    channel = NULL;
     goto out;
   }
 
-  /* Get channel ID */
-  tmp = silc_channel_key_get_id(payload, &tmp_len);
-  id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!id)
-    goto out;
-
   /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
   if (!channel) {
-    SILC_LOG_ERROR(("Received key for non-existent channel"));
-    goto out;
+
+    /* Get channel ID */
+    tmp = silc_channel_key_get_id(payload, &tmp_len);
+    id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
+    if (!id) {
+      channel = NULL;
+      goto out;
+    }
+
+    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);
+      if (!channel) {
+       SILC_LOG_ERROR(("Received key for non-existent channel"));
+       goto out;
+      }
+    }
   }
 
-  /* Save the key for us as well */
   tmp = silc_channel_key_get_key(payload, &tmp_len);
-  if (!tmp)
+  if (!tmp) {
+    channel = NULL;
     goto out;
-  cipher = silc_channel_key_get_cipher(payload, NULL);;
-  if (!cipher)
+  }
+
+  cipher = silc_channel_key_get_cipher(payload, NULL);
+  if (!cipher) {
+    channel = NULL;
     goto out;
-  if (!silc_cipher_alloc(cipher, &channel->channel_key))
+  }
+
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+    silc_cipher_free(channel->channel_key);
+  }
+
+  /* Create new cipher */
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel = NULL;
     goto out;
+  }
+
+  if (channel->cipher)
+    silc_free(channel->cipher);
+  channel->cipher = strdup(cipher);
 
+  /* Save the key */
   channel->key_len = tmp_len * 8;
   channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
   memcpy(channel->key, tmp, tmp_len);
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       tmp, tmp_len);
+  silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
 
-  /* Distribute the key to all clients on the channel */
-  /* XXX Some other sender should be used, I think this is not correct */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    client = chl->client;
+  /* 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));
+
+  memset(hash, 0, sizeof(hash));
+  memset(tmp, 0, tmp_len);
 
-    if (client)
-      silc_server_packet_send_dest(server, client->connection,
-                                  SILC_PACKET_CHANNEL_KEY, 0,
-                                  client->id, SILC_ID_CLIENT,
-                                  buffer->data, buffer->len, FALSE);
+  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);
   }
 
  out:
@@ -2675,824 +3053,694 @@ void silc_server_channel_key(SilcServer server,
     silc_free(id);
   if (payload)
     silc_channel_key_payload_free(payload);
-  silc_buffer_free(buffer);
+
+  return channel;
 }
 
-/* Sends current motd to client */
+/* 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_send_motd(SilcServer server,
-                          SilcSocketConnection sock)
+void silc_server_perform_heartbeat(SilcSocketConnection sock,
+                                  void *hb_context)
 {
-  char *motd;
-  int motd_len;
+  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
 
-  if (server->config && server->config->motd && 
-      server->config->motd->motd_file) {
+  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
+                 sock->ip));
 
-    motd = silc_file_read(server->config->motd->motd_file, &motd_len);
-    if (!motd)
-      return;
-
-    silc_server_send_notify(server, sock, SILC_NOTIFY_TYPE_MOTD, 1,
-                           motd, motd_len);
-    silc_free(motd);
-  }
+  /* Send the heartbeat */
+  silc_server_send_heartbeat(hb->server, sock);
 }
 
-/* Sends error message. Error messages may or may not have any 
-   implications. */
+/* Returns assembled of all servers in the given ID list. The packet's
+   form is dictated by the New ID payload. */
 
-void silc_server_send_error(SilcServer server,
-                           SilcSocketConnection sock,
-                           const char *fmt, ...)
+static void silc_server_announce_get_servers(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *servers)
 {
-  va_list ap;
-  unsigned char buf[4096];
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+  SilcBuffer idp;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  /* 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;
+      }
+    }
 
-  silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
-                         buf, strlen(buf), FALSE);
+    silc_idcache_list_free(list);
+  }
 }
 
-/* Sends notify message. If format is TRUE the variable arguments are
-   formatted and the formatted string is sent as argument payload. If it is
-   FALSE then each argument is sent as separate argument and their format
-   in the argument list must be { argument data, argument length }. */
+/* This function is used by router to announce existing servers to our
+   primary router when we've connected to it. */
 
-void silc_server_send_notify(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcNotifyType type,
-                            unsigned int argc, ...)
+void silc_server_announce_servers(SilcServer server)
 {
-  va_list ap;
-  SilcBuffer packet;
+  SilcBuffer servers = NULL;
 
-  va_start(ap, argc);
+  SILC_LOG_DEBUG(("Announcing servers"));
 
-  packet = silc_notify_payload_encode(type, argc, ap);
-  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, 
-                         packet->data, packet->len, FALSE);
-  silc_buffer_free(packet);
-}
+  /* Get servers in local list */
+  silc_server_announce_get_servers(server, server->local_list, &servers);
 
-/* Sends notify message destined to specific entity. */
+  /* Get servers in global list */
+  silc_server_announce_get_servers(server, server->global_list, &servers);
 
-void silc_server_send_notify_dest(SilcServer server,
-                                 SilcSocketConnection sock,
-                                 void *dest_id,
-                                 SilcIdType dest_id_type,
-                                 SilcNotifyType type,
-                                 unsigned int argc, ...)
-{
-  va_list ap;
-  SilcBuffer packet;
+  if (servers) {
+    silc_buffer_push(servers, servers->data - servers->head);
+    SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
 
-  va_start(ap, argc);
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
+                           servers->data, servers->len, TRUE);
 
-  packet = silc_notify_payload_encode(type, argc, ap);
-  silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 0, 
-                              dest_id, dest_id_type,
-                              packet->data, packet->len, FALSE);
-  silc_buffer_free(packet);
+    silc_buffer_free(servers);
+  }
 }
 
-/* Sends notify message to a channel. The notify message sent is 
-   distributed to all clients on the channel. */
+/* Returns assembled packet of all clients in the given ID list. The
+   packet's form is dictated by the New ID Payload. */
 
-void silc_server_send_notify_to_channel(SilcServer server,
-                                       SilcChannelEntry channel,
-                                       SilcNotifyType type,
-                                       unsigned int argc, ...)
+static void silc_server_announce_get_clients(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *clients)
 {
-  va_list ap;
-  SilcBuffer packet;
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcClientEntry client;
+  SilcBuffer idp;
 
-  va_start(ap, argc);
+  /* 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;
+      }
+    }
 
-  packet = silc_notify_payload_encode(type, argc, ap);
-  silc_server_packet_send_to_channel(server, channel, 
-                                    SILC_PACKET_NOTIFY,
-                                    packet->data, packet->len, FALSE);
-  silc_buffer_free(packet);
+    silc_idcache_list_free(list);
+  }
 }
 
-/* Send notify message to all clients the client has joined. It is quaranteed
-   that the message is sent only once to a client (ie. if a client is joined
-   on two same channel it will receive only one notify message). Also, this
-   sends only to local clients (locally connected if we are server, and to
-   local servers if we are router). */
+/* This function is used to announce our existing clients to our router
+   when we've connected to it. */
 
-void silc_server_send_notify_on_channels(SilcServer server,
-                                        SilcClientEntry client,
-                                        SilcNotifyType type,
-                                        unsigned int argc, ...)
+void silc_server_announce_clients(SilcServer server)
 {
-  int k;
-  SilcSocketConnection sock = NULL;
-  SilcPacketContext packetdata;
-  SilcClientEntry c;
-  SilcClientEntry *sent_clients = NULL;
-  unsigned int sent_clients_count = 0;
-  SilcServerEntry *routed = NULL;
-  unsigned int routed_count = 0;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl, chl2;
-  SilcCipher cipher;
-  SilcHmac hmac;
-  SilcBuffer packet;
-  unsigned char *data;
-  unsigned int data_len;
-  int force_send = FALSE;
-  va_list ap;
-
-  if (!silc_list_count(client->channels))
-    return;
-
-  va_start(ap, argc);
-  packet = silc_notify_payload_encode(type, argc, ap);
-  data = packet->data;
-  data_len = packet->len;
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_NOTIFY;
-  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
-  packetdata.src_id_len = SILC_ID_SERVER_LEN;
-  packetdata.src_id_type = SILC_ID_SERVER;
-  packetdata.rng = server->rng;
+  SilcBuffer clients = NULL;
 
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
-    channel = chl->channel;
+  SILC_LOG_DEBUG(("Announcing clients"));
 
-    /* Send the message to all clients on the channel's client list. */
-    silc_list_start(channel->user_list);
-    while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-      c = chl2->client;
-      
-      /* Check if we have sent the packet to this client already */
-      for (k = 0; k < sent_clients_count; k++)
-       if (sent_clients[k] == c)
-         break;
-      if (k < sent_clients_count)
-       continue;
+  /* Get clients in local list */
+  silc_server_announce_get_clients(server, server->local_list,
+                                  &clients);
 
-      /* If we are router and if this client has router set it is not
-        locally connected client and we will route the message to the
-        router set in the client. */
-      if (c && c->router && server->server_type == SILC_ROUTER) {
-       /* Check if we have sent the packet to this route already */
-       for (k = 0; k < routed_count; k++)
-         if (routed[k] == c->router)
-           break;
-       if (k < routed_count)
-         continue;
-       
-       /* Get data used in packet header encryption, keys and stuff. */
-       sock = (SilcSocketConnection)c->router->connection;
-       cipher = c->router->send_key;
-       hmac = c->router->hmac;
-       
-       packetdata.dst_id = silc_id_id2str(c->router->id, SILC_ID_SERVER);
-       packetdata.dst_id_len = SILC_ID_SERVER_LEN;
-       packetdata.dst_id_type = SILC_ID_SERVER;
-       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-         packetdata.src_id_len + packetdata.dst_id_len;
-       packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-
-       /* Send the packet */
-       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                               cipher, hmac, data,
-                                               data_len, FALSE, force_send);
-       
-       silc_free(packetdata.dst_id);
+  /* 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);
 
-       /* We want to make sure that the packet is routed to same router
-          only once. Mark this route as sent route. */
-       k = routed_count;
-       routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
-       routed[k] = c->router;
-       routed_count++;
+  if (clients) {
+    silc_buffer_push(clients, clients->data - clients->head);
+    SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
 
-       continue;
-      }
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
+                           clients->data, clients->len, TRUE);
 
-      /* Send to locally connected client */
-      if (c) {
-       
-       /* Get data used in packet header encryption, keys and stuff. */
-       sock = (SilcSocketConnection)c->connection;
-       cipher = c->send_key;
-       hmac = c->hmac;
-       
-       packetdata.dst_id = silc_id_id2str(c->id, SILC_ID_CLIENT);
-       packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
-       packetdata.dst_id_type = SILC_ID_CLIENT;
-       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-         packetdata.src_id_len + packetdata.dst_id_len;
-       packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-
-       /* Send the packet */
-       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                               cipher, hmac, data,
-                                               data_len, FALSE, force_send);
-
-       silc_free(packetdata.dst_id);
-
-       /* Make sure that we send the notify only once per client. */
-       sent_clients = silc_realloc(sent_clients, sizeof(*sent_clients) * 
-                                   (sent_clients_count + 1));
-       sent_clients[sent_clients_count] = c;
-       sent_clients_count++;
-      }
-    }
+    silc_buffer_free(clients);
   }
-
-  if (routed_count)
-    silc_free(routed);
-  if (sent_clients_count)
-    silc_free(sent_clients);
-  silc_free(packetdata.src_id);
 }
 
-/* Sends New ID Payload to remote end. The packet is used to distribute
-   information about new registered clients, servers, channel etc. usually
-   to routers so that they can keep these information up to date. 
-   If the argument `broadcast' is TRUE then the packet is sent as
-   broadcast packet. */
-
-void silc_server_send_new_id(SilcServer server,
-                            SilcSocketConnection sock,
-                            int broadcast,
-                            void *id, SilcIdType id_type, 
-                            unsigned int id_len)
+static SilcBuffer 
+silc_server_announce_encode_join(uint32 argc, ...)
 {
-  SilcBuffer idp;
+  va_list ap;
 
-  idp = silc_id_payload_encode(id, id_type);
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         idp->data, idp->len, FALSE);
-  silc_buffer_free(idp);
+  va_start(ap, argc);
+  return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
 }
 
-/* Sends Replace ID payload to remote end. This is used to replace old
-   ID with new ID sent in the packet.  This is called for example when
-   user changes nickname and we create new ID for the user.  If the 
-   argument `broadcast' is TRUE then the packet is sent as
-   broadcast packet. */
-/* XXX It would be expected that the new id is same type as the old
-   ID. :) */
-
-void silc_server_send_replace_id(SilcServer server,
-                                SilcSocketConnection sock,
-                                int broadcast,
-                                void *old_id, SilcIdType old_id_type,
-                                unsigned int old_id_len,
-                                void *new_id, SilcIdType new_id_type,
-                                unsigned int new_id_len)
-{
-  SilcBuffer packet;
-  unsigned char *oid;
-  unsigned char *nid;
-
-  oid = silc_id_id2str(old_id, old_id_type);
-  if (!oid)
-    return;
+/* Returns assembled packets for channel users of the `channel'. */
 
-  nid = silc_id_id2str(new_id, new_id_type);
-  if (!nid)
-    return;
+void silc_server_announce_get_channel_users(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *channel_users)
+{
+  SilcChannelClientEntry chl;
+  SilcBuffer chidp, clidp;
+  SilcBuffer tmp;
+  int len;
 
-  packet = silc_buffer_alloc(2 + 2 + 2 + 2 + old_id_len + new_id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(old_id_type),
-                    SILC_STR_UI_SHORT(old_id_len),
-                    SILC_STR_UI_XNSTRING(oid, old_id_len),
-                    SILC_STR_UI_SHORT(new_id_type),
-                    SILC_STR_UI_SHORT(new_id_len),
-                    SILC_STR_UI_XNSTRING(nid, new_id_len),
-                    SILC_STR_END);
+  SILC_LOG_DEBUG(("Start"));
 
-  silc_server_packet_send(server, sock, SILC_PACKET_REPLACE_ID, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(oid);
-  silc_free(nid);
-  silc_buffer_free(packet);
+  /* 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) {
+    clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+    tmp = silc_server_announce_encode_join(2, clidp->data, clidp->len,
+                                          chidp->data, chidp->len);
+    len = tmp->len;
+    *channel_users = 
+      silc_buffer_realloc(*channel_users, 
+                         (*channel_users ? 
+                          (*channel_users)->truelen + len : len));
+    silc_buffer_pull_tail(*channel_users, 
+                         ((*channel_users)->end - 
+                          (*channel_users)->data));
+    
+    silc_buffer_put(*channel_users, tmp->data, tmp->len);
+    silc_buffer_pull(*channel_users, len);
+    silc_buffer_free(clidp);
+    silc_buffer_free(tmp);
+  }
+  silc_buffer_free(chidp);
 }
 
-/* This function is used to send Remove Channel User payload. This may sent
-   by server but is usually used only by router to notify other routers that
-   user has left a channel. Normal server sends this packet to its router
-   to notify that the router should not hold a record about this client
-   on a channel anymore. Router distributes it further to other routers. */
+/* Returns assembled packets for all channels and users on those channels
+   from the given ID List. The packets are in the form dictated by the
+   New Channel and New Channel User payloads. */
 
-void silc_server_send_remove_channel_user(SilcServer server,
-                                         SilcSocketConnection sock,
-                                         int broadcast,
-                                         void *client_id, void *channel_id)
+void silc_server_announce_get_channels(SilcServer server,
+                                      SilcIDList id_list,
+                                      SilcBuffer *channels,
+                                      SilcBuffer *channel_users)
 {
-  SilcBuffer packet;
-  unsigned char *clid, *chid;
-
-  clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
-  if (!clid)
-    return;
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry channel;
+  unsigned char *cid;
+  uint16 name_len;
+  int len;
 
-  chid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
-  if (!chid)
-    return;
+  SILC_LOG_DEBUG(("Start"));
 
-  packet = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN + SILC_ID_CHANNEL_LEN);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
-                    SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
-                    SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                    SILC_STR_UI_XNSTRING(chid, SILC_ID_CHANNEL_LEN),
-                    SILC_STR_END);
+  /* 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);
+       name_len = strlen(channel->channel_name);
+
+       len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+       *channels = 
+         silc_buffer_realloc(*channels, 
+                             (*channels ? (*channels)->truelen + len : len));
+       silc_buffer_pull_tail(*channels, 
+                             ((*channels)->end - (*channels)->data));
+       silc_buffer_format(*channels,
+                          SILC_STR_UI_SHORT(name_len),
+                          SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                               name_len),
+                          SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                          SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                          SILC_STR_UI_INT(channel->mode),
+                          SILC_STR_END);
+       silc_buffer_pull(*channels, len);
+
+       silc_server_announce_get_channel_users(server, channel,
+                                              channel_users);
+
+       silc_free(cid);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
 
-  silc_server_packet_send(server, sock, SILC_PACKET_REMOVE_CHANNEL_USER, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(clid);
-  silc_free(chid);
-  silc_buffer_free(packet);
+    silc_idcache_list_free(list);
+  }
 }
 
-/* Received packet to replace a ID. This checks that the requested ID
-   exists and replaces it with the new one. */
+/* 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_replace_id(SilcServer server,
-                           SilcSocketConnection sock,
-                           SilcPacketContext *packet)
+void silc_server_announce_channels(SilcServer server)
 {
-  SilcBuffer buffer = packet->buffer;
-  unsigned char *old_id = NULL, *new_id = NULL;
-  SilcIdType old_id_type, new_id_type;
-  unsigned short old_id_len, new_id_len;
-  void *id = NULL, *id2 = NULL;
-
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type == SILC_ID_CLIENT)
-    return;
+  SilcBuffer channels = NULL, channel_users = NULL;
 
-  SILC_LOG_DEBUG(("Replacing ID"));
+  SILC_LOG_DEBUG(("Announcing channels and channel users"));
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&old_id_type),
-                      SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len),
-                      SILC_STR_UI_SHORT(&new_id_type),
-                      SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len),
-                      SILC_STR_END);
+  /* Get channels and channel users in local list */
+  silc_server_announce_get_channels(server, server->local_list,
+                                   &channels, &channel_users);
 
-  if (old_id_type != new_id_type)
-    goto out;
+  /* Get channels and channel users in global list */
+  silc_server_announce_get_channels(server, server->global_list,
+                                   &channels, &channel_users);
 
-  if (old_id_len != silc_id_get_len(old_id_type) ||
-      new_id_len != silc_id_get_len(new_id_type))
-    goto out;
+  if (channels) {
+    silc_buffer_push(channels, channels->data - channels->head);
+    SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
 
-  id = silc_id_str2id(old_id, old_id_type);
-  if (!id)
-    goto out;
-
-  id2 = silc_id_str2id(new_id, new_id_type);
-  if (!id2)
-    goto out;
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
+                           channels->data, channels->len,
+                           FALSE);
 
-  /* Replace the old ID */
-  switch(old_id_type) {
-  case SILC_ID_CLIENT:
-    if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
-      if (server->server_type == SILC_ROUTER)
-       silc_idlist_replace_client_id(server->global_list, id, id2);
-    break;
+    silc_buffer_free(channels);
+  }
 
-  case SILC_ID_SERVER:
-    if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
-      if (server->server_type == SILC_ROUTER)
-       silc_idlist_replace_server_id(server->global_list, id, id2);
-    break;
+  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);
 
-  case SILC_ID_CHANNEL:
-    /* XXX Hmm... Basically this cannot occur. Channel ID's cannot be
-       re-generated. */
-    silc_free(id2);
-    break;
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           channel_users->data, channel_users->len,
+                           FALSE);
 
-  default:
-    silc_free(id2);
-    break;
+    silc_buffer_free(channel_users);
   }
-
- out:
-  if (id)
-    silc_free(id);
-  if (old_id)
-    silc_free(old_id);
-  if (new_id)
-    silc_free(new_id);
 }
 
-/* Creates new channel. */
+/* 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). */
 
-SilcChannelEntry silc_server_new_channel(SilcServer server, 
-                                        SilcServerID *router_id,
-                                        char *cipher, char *channel_name)
+SILC_TASK_CALLBACK(silc_server_failure_callback)
 {
-  int i, channel_len, key_len;
-  SilcChannelID *channel_id;
-  SilcChannelEntry entry;
-  SilcCipher key;
-  unsigned char channel_key[32], *id_string;
-  SilcBuffer packet;
-
-  SILC_LOG_DEBUG(("Creating new channel"));
+  SilcServerFailureContext f = (SilcServerFailureContext)context;
 
-  /* Create channel key */
-  for (i = 0; i < 32; i++)
-    channel_key[i] = silc_rng_get_byte(server->rng);
-
-  if (!cipher)
-    cipher = "twofish";
+  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);
+  }
 
-  /* Allocate keys */
-  key_len = 16;
-  silc_cipher_alloc(cipher, &key);
-  key->cipher->set_key(key->context, channel_key, key_len);
+  silc_free(f);
+}
 
-  channel_name = strdup(channel_name);
+/* Assembles user list and users mode list from the `channel'. */
 
-  /* 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);
-  if (!entry) {
-    silc_free(channel_name);
-    return NULL;
-  }
-
-  entry->key = silc_calloc(key_len, sizeof(*entry->key));
-  entry->key_len = key_len * 8;
-  memcpy(entry->key, channel_key, key_len);
-  memset(channel_key, 0, sizeof(channel_key));
+void silc_server_get_users_on_channel(SilcServer server,
+                                     SilcChannelEntry channel,
+                                     SilcBuffer *user_list,
+                                     SilcBuffer *mode_list,
+                                     uint32 *user_count)
+{
+  SilcChannelClientEntry chl;
+  SilcBuffer client_id_list;
+  SilcBuffer client_mode_list;
+  SilcBuffer idp;
+  uint32 list_count = 0;
+
+  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) {
+    /* 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_free(idp);
 
-  /* Notify other routers about the new channel. We send the packet
-     to our primary route. */
-  if (server->standalone == FALSE) {
-    channel_len = strlen(channel_name);
-    id_string = silc_id_id2str(entry->id, SILC_ID_CHANNEL);
-    packet = silc_buffer_alloc(2 + SILC_ID_CHANNEL_LEN);
-
-    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-    silc_buffer_format(packet,
-                      SILC_STR_UI_SHORT(channel_len),
-                      SILC_STR_UI_XNSTRING(channel_name, channel_len),
-                      SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                      SILC_STR_UI_XNSTRING(id_string, SILC_ID_CHANNEL_LEN),
-                      SILC_STR_END);
+    /* Client's mode on channel */
+    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
+    silc_buffer_pull(client_mode_list, 4);
 
-    /* Send the packet to our router. */
-    silc_server_packet_send(server, (SilcSocketConnection) 
-                           server->id_entry->router->connection,
-                           SILC_PACKET_NEW_CHANNEL_USER, 0, 
-                           packet->data, packet->len, TRUE);
-    
-    silc_free(id_string);
-    silc_buffer_free(packet);
+    list_count++;
   }
-
-  return entry;
+  silc_buffer_push(client_id_list, 
+                  client_id_list->data - client_id_list->head);
+  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;
 }
 
-/* Create new client. This processes incoming NEW_CLIENT packet and creates
-   Client ID for the client. Client becomes registered after calling this
-   functions. */
+/* Saves users and their modes to the `channel'. */
 
-SilcClientEntry silc_server_new_client(SilcServer server,
+void silc_server_save_users_on_channel(SilcServer server,
                                       SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+                                      SilcChannelEntry channel,
+                                      SilcClientID *noadd,
+                                      SilcBuffer user_list,
+                                      SilcBuffer mode_list,
+                                      uint32 user_count)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client;
-  SilcIDCacheEntry cache;
-  SilcClientID *client_id;
-  SilcBuffer reply;
-  char *username = NULL, *realname = NULL, *id_string;
-
-  SILC_LOG_DEBUG(("Creating new client"));
+  int i;
+
+  /* Cache the received Client ID's and modes. This cache expires
+     whenever server sends notify message to channel. It means two things;
+     some user has joined or leaved the channel. XXX TODO! */
+  for (i = 0; i < user_count; i++) {
+    uint16 idp_len;
+    uint32 mode;
+    SilcClientID *client_id;
+    SilcClientEntry client;
+
+    /* 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)
+      continue;
 
-  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
-    return NULL;
+    /* Mode */
+    SILC_GET32_MSB(mode, mode_list->data);
+    silc_buffer_pull(mode_list, 4);
 
-  /* Take client entry */
-  client = (SilcClientEntry)sock->user_data;
+    if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+      silc_free(client_id);
+      continue;
+    }
+    
+    /* Check if we have this client cached already. */
+    client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                          NULL);
+    if (!client)
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, NULL);
+    if (!client) {
+      /* If router did not find such Client ID in its lists then this must
+        be bogus client or some router in the net is buggy. */
+      if (server->server_type == SILC_ROUTER) {
+       silc_free(client_id);
+       continue;
+      }
 
-  /* Fetch the old client cache entry so that we can update it. */
-  if (!silc_idcache_find_by_context(server->local_list->clients,
-                                   sock->user_data, &cache)) {
-    SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
-    return NULL;
-  }
+      /* We don't have that client anywhere, add it. The client is added
+        to global list since server didn't have it in the lists so it must be 
+        global. */
+      client = silc_idlist_add_client(server->global_list, NULL, 0, NULL, 
+                                     NULL, 
+                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
+                                     sock->user_data, NULL);
+      if (!client) {
+       silc_free(client_id);
+       continue;
+      }
 
-  /* Parse incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&username),
-                      SILC_STR_UI16_STRING_ALLOC(&realname),
-                      SILC_STR_END);
+      client->data.registered = TRUE;
+    }
 
-  /* Create Client ID */
-  silc_id_create_client_id(server->id, server->rng, server->md5hash,
-                          username, &client_id);
-
-  /* Update client entry */
-  client->registered = TRUE;
-  client->nickname = strdup(username);
-  client->username = username;
-  client->userinfo = realname;
-  client->id = client_id;
-
-  /* Update the cache entry */
-  cache->id = (void *)client_id;
-  cache->type = SILC_ID_CLIENT;
-  cache->data = username;
-  silc_idcache_sort_by_data(server->local_list->clients);
-
-  /* Notify our router about new client on the SILC network */
-  if (!server->standalone)
-    silc_server_send_new_id(server, (SilcSocketConnection) 
-                           server->id_entry->router->connection, 
-                           server->server_type == SILC_SERVER ? TRUE : FALSE,
-                           client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
-  
-  /* Send the new client ID to the client. */
-  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
-  reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
-  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
-  silc_buffer_format(reply,
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT),
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
-                    SILC_STR_END);
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
-                         reply->data, reply->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(reply);
-
-  /* Send some nice info to the client */
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Welcome to the SILC Network %s@%s",
-                          username, sock->hostname));
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your host is %s, running version %s",
-                          server->config->server_info->server_name,
-                          server_version));
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your connection is secured with %s cipher, "
-                          "key length %d bits",
-                          client->send_key->cipher->name,
-                          client->send_key->cipher->key_len));
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your current nickname is %s",
-                          client->nickname));
-
-  /* Send motd */
-  silc_server_send_motd(server, sock);
+    silc_free(client_id);
 
-  return client;
+    if (!silc_server_client_on_channel(client, channel)) {
+      /* Client was not on the channel, add it. */
+      SilcChannelClientEntry 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);
+    }
+  }
 }
 
-/* Create new server. This processes incoming NEW_SERVER packet and
-   saves the received Server ID. The server is our locally connected
-   server thus we save all the information and save it to local list. 
-   This funtion can be used by both normal server and router server.
-   If normal server uses this it means that its router has connected
-   to the server. If router uses this it means that one of the cell's
-   servers is connected to the router. */
+/* Lookups route to the client indicated by the `id_data'. The connection
+   object and internal data object is returned. Returns NULL if route
+   could not be found to the client. If the `client_id' is specified then
+   it is used and the `id_data' is ignored. */
 
-SilcServerEntry silc_server_new_server(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+                                                 unsigned char *id_data,
+                                                 uint32 id_len,
+                                                 SilcClientID *client_id,
+                                                 SilcIDListData *idata)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcServerEntry new_server;
-  SilcIDCacheEntry cache;
-  SilcServerID *server_id;
-  unsigned char *server_name, *id_string;
-  unsigned short id_len;
+  SilcClientID *id;
+  SilcClientEntry client;
 
-  SILC_LOG_DEBUG(("Creating new server"));
+  SILC_LOG_DEBUG(("Start"));
 
-  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return 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"));
+      return NULL;
+    }
+  } else {
+    id = silc_id_dup(client_id, SILC_ID_CLIENT);
+  }
 
-  /* Take server entry */
-  new_server = (SilcServerEntry)sock->user_data;
+  /* 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);
+  if (client) {
+    silc_free(id);
 
-  /* Fetch the old server cache entry so that we can update it. */
-  if (!silc_idcache_find_by_context(server->local_list->servers,
-                                   sock->user_data, &cache)) {
-    SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
-    return NULL;
-  }
+    if (client && client->data.registered == FALSE)
+      return NULL;
 
-  /* Parse the incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
-                      SILC_STR_UI16_STRING_ALLOC(&server_name),
-                      SILC_STR_END);
+    /* 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. */
+      if (idata)
+       *idata = (SilcIDListData)client->router;
+      return client->router->connection;
+    }
 
-  if (id_len > buffer->len) {
-    silc_free(id_string);
-    silc_free(server_name);
-    return NULL;
+    /* Seems that client really is directly connected to us */
+    if (idata)
+      *idata = (SilcIDListData)client;
+    return client->connection;
   }
 
-  /* Get Server ID */
-  server_id = silc_id_payload_parse_id(id_string, id_len);
-  silc_free(id_string);
+  /* 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) {
+    silc_free(id);
+    if (idata)
+      *idata = (SilcIDListData)server->router;
+    return server->router->connection;
+  }
 
-  /* Update client entry */
-  new_server->registered = TRUE;
-  new_server->server_name = server_name;
-  new_server->id = server_id;
-
-  /* Update the cache entry */
-  cache->id = (void *)server_id;
-  cache->type = SILC_ID_SERVER;
-  cache->data = server_name;
-  silc_idcache_sort_by_data(server->local_list->servers);
-
-  /* Distribute the information about new server in the SILC network
-     to our router. If we are normal server we won't send anything
-     since this connection must be our router connection. */
-  if (server->server_type == SILC_ROUTER && !server->standalone)
-    silc_server_send_new_id(server, server->id_entry->router->connection,
-                           TRUE, new_server->id, SILC_ID_SERVER, 
-                           SILC_ID_SERVER_LEN);
-
-  return new_server;
-}
+  /* 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);
+    if (client) {
+      SilcSocketConnection dst_sock;
 
-/* Processes incoming New ID Payload. New ID Payload is used to distribute
-   information about newly registered clients, servers and created 
-   channels. */
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
 
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
-                       SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcIDList id_list;
-  SilcServerEntry tmpserver, router;
-  SilcSocketConnection router_sock;
-  SilcIDPayload idp;
-  SilcIdType id_type;
-  void *id, *tmpid;
-
-  SILC_LOG_DEBUG(("Processing new ID"));
-
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
+      silc_free(id);
+      if (idata)
+       *idata = (SilcIDListData)dst_sock->user_data;
+      return dst_sock;
+    }
+  }
 
-  idp = silc_id_payload_parse(buffer);
-  if (!idp)
-    return;
+  silc_free(id);
+  return NULL;
+}
 
-  id_type = silc_id_payload_get_type(idp);
+/* Encodes and returns channel list of channels the `client' has joined.
+   Secret channels are not put to the list. */
 
-  /* Normal server cannot have other normal server connections */
-  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
-    goto out;
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+                                              SilcClientEntry client)
+{
+  SilcBuffer buffer = NULL;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  unsigned char *cid;
+  uint16 name_len;
+  int len;
 
-  id = silc_id_payload_get_id(idp);
-  if (!id)
-    goto out;
+  silc_list_start(client->channels);
+  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+    channel = chl->channel;
 
-  /* If the packet is originated from the one who sent it to us we know
-     that the ID belongs to our cell, unless the sender was router. */
-  tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
-  tmpserver = (SilcServerEntry)sock->user_data;
-
-  if (!SILC_ID_SERVER_COMPARE(tmpid, tmpserver->id) &&
-      sock->type == SILC_SOCKET_TYPE_SERVER) {
-    id_list = server->local_list;
-    router_sock = sock;
-    router = sock->user_data;
-    /*    router = server->id_entry; */
-  } else {
-    id_list = server->global_list;
-    router_sock = (SilcSocketConnection)server->id_entry->router->connection;
-    router = server->id_entry->router;
+    if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+      continue;
+
+    cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+    name_len = strlen(channel->channel_name);
+    
+    len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+    buffer = silc_buffer_realloc(buffer, 
+                                (buffer ? (buffer)->truelen + len : len));
+    silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_SHORT(name_len),
+                      SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                           name_len),
+                      SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                      SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                      SILC_STR_UI_INT(chl->mode), /* Client's mode */
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, len);
+    silc_free(cid);
   }
 
-  silc_free(tmpid);
+  if (buffer)
+    silc_buffer_push(buffer, buffer->data - buffer->head);
 
-  switch(id_type) {
-  case SILC_ID_CLIENT:
-    {
-      SilcClientEntry idlist;
+  return buffer;
+}
 
-      /* Add the client to our local list. We are router and we keep
-        cell specific local database of all clients in the cell. */
-      idlist = silc_idlist_add_client(id_list, NULL, NULL, NULL,
-                                     id, router, NULL, NULL, 
-                                     NULL, NULL, NULL, router_sock);
-    }
-    break;
+/* Finds client entry by Client ID and if it is not found then resolves
+   it using WHOIS command. */
 
-  case SILC_ID_SERVER:
-    {
-      SilcServerEntry idlist;
+SilcClientEntry silc_server_get_client_resolve(SilcServer server,
+                                              SilcClientID *client_id)
+{
+  SilcClientEntry client;
 
-      /* Add the server to our local list. We are router and we keep
-        cell specific local database of all servers in the cell. */
-      idlist = silc_idlist_add_server(id_list, NULL, 0,
-                                     id, router, NULL, NULL, 
-                                     NULL, NULL, NULL, router_sock);
-    }
-    break;
+  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;
+  }
 
-  case SILC_ID_CHANNEL:
-    /* Add the channel to our local list. We are router and we keep
-       cell specific local database of all channels in the cell. */
-    silc_idlist_add_channel(id_list, NULL, 0, id, router, NULL);
-    break;
+  if (!client && server->standalone)
+    return NULL;
 
-  default:
-    break;
+  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);
   }
 
- out:
-  silc_id_payload_free(idp);
+  return client;
 }
 
-/* Received packet to remove a user from a channel. Routers notify other
-   routers that user has left a channel. Client must not send this packet. 
-   Normal server may send this packet but ignores if it receives one. */
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
 
-void silc_server_remove_channel_user(SilcServer server,
-                                    SilcSocketConnection sock,
-                                    SilcPacketContext *packet)
+SILC_TASK_CALLBACK(silc_server_rekey_callback)
 {
-  SilcBuffer buffer = packet->buffer;
-  unsigned char *tmp1 = NULL, *tmp2 = NULL;
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
-  SilcChannelEntry channel;
-  SilcClientEntry client;
-
-  SILC_LOG_DEBUG(("Removing user from channel"));
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcServer server = (SilcServer)idata->rekey->context;
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
-    return;
+  SILC_LOG_DEBUG(("Start"));
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&tmp1),
-                      SILC_STR_UI16_STRING_ALLOC(&tmp2),
-                      SILC_STR_END);
+  /* 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);
+}
 
-  if (!tmp1 || !tmp2)
-    goto out;
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
 
-  client_id = silc_id_str2id(tmp1, SILC_ID_CLIENT);
-  channel_id = silc_id_str2id(tmp2, SILC_ID_CHANNEL);
-  if (!client_id || !channel_id)
-    goto out;
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx =
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+  SilcSocketConnection sock = ctx->sock;
 
-  /* XXX routers should check server->global_list as well */
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
-  if (!channel)
-    goto out;
-  
-  /* XXX routers should check server->global_list as well */
-  /* Get client entry */
-  client = silc_idlist_find_client_by_id(server->local_list, client_id);
-  if (!client)
-    goto out;
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Remove from channel */
-  silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+  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;
+  }
 
- out:
-  if (tmp1)
-    silc_free(tmp1);
-  if (tmp2)
-    silc_free(tmp2);
-  if (client_id)
-    silc_free(client_id);
-  if (channel_id)
-    silc_free(channel_id);
+  /* 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);
 }