updates.
[silc.git] / apps / silcd / server.c
index 0d351bc00df39d9e430f6735042e9ee61870f23e..eaa5eaf4f7abd23e0cdcfafe8ef2c89948eae861 100644 (file)
@@ -38,6 +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);
+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
@@ -96,7 +98,6 @@ void silc_server_free(SilcServer server)
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
 
-    silc_math_primegen_uninit(); /* XXX */
     silc_free(server);
   }
 }
@@ -113,11 +114,22 @@ 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));
@@ -130,17 +142,18 @@ int silc_server_init(SilcServer server)
 
   /* 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);
@@ -149,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. */
@@ -220,16 +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);
+  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);
-  server->global_list->servers = silc_idcache_alloc(0);
-  server->global_list->channels = silc_idcache_alloc(0);
+  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 
@@ -274,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. */
@@ -334,6 +312,27 @@ int silc_server_init(SilcServer server)
   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 */
@@ -534,6 +533,9 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   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);
@@ -623,7 +625,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
-      sconn->remote_host = server->config->routers->host;
+      sconn->remote_host = strdup(server->config->routers->host);
       sconn->remote_port = server->config->routers->port;
 
       silc_task_register(server->timeout_queue, fd, 
@@ -637,7 +639,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   /* 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"));
 
@@ -653,7 +655,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
        /* Allocate connection object for hold connection specific stuff. */
        sconn = silc_calloc(1, sizeof(*sconn));
        sconn->server = server;
-       sconn->remote_host = ptr->host;
+       sconn->remote_host = strdup(ptr->host);
        sconn->remote_port = ptr->port;
 
        silc_task_register(server->timeout_queue, fd, 
@@ -686,14 +688,18 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
-  SilcSocketConnection sock = NULL;
+  SilcSocketConnection sock = 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_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -701,44 +707,80 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     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->context = (void *)sconn;
-  proto_ctx->sock = sock = server->sockets[fd];
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;      /* Save SKE object from previous protocol */
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
 
-  /* Resolve the authentication method used in this connection */
-  proto_ctx->auth_meth = SILC_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 */
@@ -786,10 +828,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   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 */
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
@@ -833,7 +877,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add the connected router to local server list */
   server->standalone = FALSE;
-  id_entry = silc_idlist_add_server(server->local_list, sock->hostname,
+  id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname),
                                    SILC_ROUTER, ctx->dest_id, NULL, sock);
   if (!id_entry) {
     if (ctx->dest_id)
@@ -849,7 +893,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   sock->type = SILC_SOCKET_TYPE_ROUTER;
   server->id_entry->router = id_entry;
   server->router = id_entry;
-  server->router->data.registered = TRUE;
+  idata = (SilcIDListData)sock->user_data;
+  idata->registered = TRUE;
 
   /* Perform keepalive. The `hb_context' will be freed automatically
      when finally calling the silc_socket_free function. XXX hardcoded 
@@ -860,6 +905,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
                            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);
@@ -870,8 +923,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
  out:
   /* Free the temporary connection data context */
-  if (sconn)
+  if (sconn) {
+    silc_free(sconn->remote_host);
     silc_free(sconn);
+  }
 
   /* Free the protocol object */
   silc_protocol_free(protocol);
@@ -891,7 +946,9 @@ 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;
+  SilcServerConfigSectionDenyConnection *deny;
 
   SILC_LOG_DEBUG(("Accepting new connection"));
 
@@ -906,13 +963,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   /* Check max connections */
   if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    if (server->config->redirect) {
-      /* XXX Redirecting connection to somewhere else now?? */
-      /*silc_server_send_notify("Server is full, trying to redirect..."); */
-    } else {
-      SILC_LOG_ERROR(("Refusing connection, server is full"));
-      server->stat.conn_failures++;
-    }
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
     return;
   }
 
@@ -929,16 +981,74 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      process. Either we have to do our own resolver stuff or in the future
      we can use threads. */
   /* Perform name and address lookups for the remote host. */
-  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-      !newsocket->ip) {
-    SILC_LOG_ERROR(("IP/DNS lookup failed"));
+  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);
+
+  port = server->sockets[fd]->port; /* Listenning port */
+
+  /* Check whether this connection is denied to connect to us. */
+  deny = silc_server_config_denied_conn(server->config, newsocket->ip, port);
+  if (!deny)
+    deny = silc_server_config_denied_conn(server->config, newsocket->hostname,
+                                         port);
+  if (deny) {
+    /* The connection is denied */
+    silc_server_disconnect_remote(server, newsocket, deny->comment ?
+                                 deny->comment :
+                                 "Server closed connection: "
+                                 "Connection refused");
     server->stat.conn_failures++;
     return;
   }
-  if (!newsocket->hostname)
-    newsocket->hostname = strdup(newsocket->ip);
-  newsocket->port = silc_net_get_remote_port(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. */
+  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));
@@ -950,6 +1060,9 @@ 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
@@ -970,14 +1083,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
@@ -992,14 +1097,17 @@ 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_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -1007,23 +1115,55 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     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);
@@ -1067,9 +1207,11 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
   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_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -1079,6 +1221,8 @@ 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++;
@@ -1099,7 +1243,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         and other information is created after we have received NEW_CLIENT
         packet from client. */
       client = silc_idlist_add_client(server->local_list, 
-                                     NULL, NULL, NULL, NULL, NULL, sock);
+                                     NULL, 0, NULL, NULL, NULL, NULL, sock);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
@@ -1119,6 +1263,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry new_server;
+      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 ? 
@@ -1151,12 +1297,14 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       server->stat.servers++;
 
       id_entry = (void *)new_server;
-      
-      /* There is connection to other server now, if it is router then
-        we will have connection to outside world.  If we are router but
-        normal server connected to us then we will remain standalone,
-        if we are standlone. */
+
+      /* 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) {
@@ -1164,6 +1312,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          server->router = id_entry;
        }
       }
+
       break;
     }
   default:
@@ -1190,6 +1339,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
                            silc_server_perform_heartbeat,
                            server->timeout_queue);
 
+  silc_task_unregister_by_callback(server->timeout_queue,
+                                  silc_server_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
@@ -1228,9 +1379,10 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     server->stat.packets_sent++;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+     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. */
@@ -1276,11 +1428,11 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
     /* If the closed connection was our primary router connection the
        start re-connecting phase. */
-    if (!server->standalone && server->server_type == SILC_SERVER && 
+    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, 0, 500000,
+                        context, 1, 0,
                         SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
 
@@ -1304,7 +1456,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   if (idata) {
     idata->last_receive = time(NULL);
     cipher = idata->receive_key;
-    hmac = idata->hmac;
+    hmac = idata->hmac_receive;
   }
  
   /* Process the packet. This will call the parser that will then
@@ -1313,6 +1465,40 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
                              server);
 }
 
+/* Callback function that the silc_packet_decrypt will call to make the
+   decision whether the packet is normal or special packet. We will 
+   return TRUE if it is normal and FALSE if it is special */
+
+static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
+                                           SilcBuffer buffer,
+                                           SilcPacketContext *packet,
+                                           void *context)
+{
+  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
+  SilcServer server = (SilcServer)parse_ctx->context;
+
+  /* Packet is normal packet, if: 
+
+     1) packet is private message packet and does not have private key set
+     2) is other packet than channel message packet
+     3) is channel message packet and remote is router and we are router 
+
+     all other packets are special packets 
+  */
+
+  if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
+      (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+    return FALSE;
+
+  if (packet_type != SILC_PACKET_CHANNEL_MESSAGE || 
+      (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
+       parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
+       server->server_type == SILC_ROUTER))
+    return TRUE;
+
+  return FALSE;
+}
+  
 /* Parses whole packet, received earlier. */
 
 SILC_TASK_CALLBACK(silc_server_packet_parse_real)
@@ -1321,13 +1507,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;
+  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, 
-                           packet->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;
 
@@ -1343,10 +1532,25 @@ 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 (!id || 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->dst_id_type == SILC_ID_SERVER && 
+    if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
+       packet->dst_id_type == SILC_ID_SERVER && 
        sock->type != SILC_SOCKET_TYPE_CLIENT &&
        SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
       
@@ -1362,20 +1566,23 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
       silc_free(id);
       goto out;
     }
-    
+  }
+
+  /* Parse the incoming packet type */
+  silc_server_packet_parse_type(server, sock, packet);
+
+  if (server->server_type == SILC_ROUTER) {
     /* Broadcast packet if it is marked as broadcast packet and it is
        originated from router and we are router. */
     if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
-       packet->flags & SILC_PACKET_FLAG_BROADCAST) {
+       packet->flags & SILC_PACKET_FLAG_BROADCAST &&
+       !server->standalone) {
       silc_server_packet_broadcast(server, server->router->connection, packet);
     }
   }
 
-  /* Parse the incoming packet type */
-  silc_server_packet_parse_type(server, sock, packet);
-
  out:
-  silc_buffer_clear(sock->inbuf);
+  /*  silc_buffer_clear(sock->inbuf); */
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
@@ -1389,8 +1596,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,
@@ -1427,6 +1634,8 @@ 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:
@@ -1436,6 +1645,8 @@ 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);
@@ -1449,16 +1660,25 @@ 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;
 
@@ -1468,7 +1688,10 @@ void silc_server_packet_parse_type(SilcServer server,
      * router. Server then relays the notify messages to clients if needed.
      */
     SILC_LOG_DEBUG(("Notify packet"));
-    silc_server_notify(server, sock, packet);
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_notify_list(server, sock, packet);
+    else
+      silc_server_notify(server, sock, packet);
     break;
 
     /* 
@@ -1481,6 +1704,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * specially.
      */
     SILC_LOG_DEBUG(("Channel Message packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_channel_message(server, sock, packet);
     break;
 
@@ -1492,6 +1717,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;
 
@@ -1504,6 +1731,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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;
 
@@ -1514,6 +1743,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * that we've routed further.
      */
     SILC_LOG_DEBUG(("Command Reply packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_command_reply(server, sock, packet);
     break;
 
@@ -1526,6 +1757,8 @@ 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;
 
@@ -1533,6 +1766,9 @@ void silc_server_packet_parse_type(SilcServer server,
     /*
      * Private message key packet.
      */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_private_message_key(server, sock, packet);
     break;
 
     /*
@@ -1540,8 +1776,11 @@ 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;
@@ -1561,26 +1800,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) {
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       SilcServerRekeyInternalContext *proto_ctx = 
+         (SilcServerRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
 
-      proto_ctx->packet = silc_packet_context_dup(packet);
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
+       /* Let the protocol handle the packet */
+       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."));
@@ -1589,26 +1846,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) {
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       SilcServerRekeyInternalContext *proto_ctx = 
+         (SilcServerRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
 
-      proto_ctx->packet = silc_packet_context_dup(packet);
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
+       /* Let the protocol handle the packet */
+       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."));
@@ -1623,6 +1898,9 @@ void silc_server_packet_parse_type(SilcServer server,
      * 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;
 
     /*
@@ -1632,6 +1910,9 @@ 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) {
 
@@ -1657,16 +1938,10 @@ void silc_server_packet_parse_type(SilcServer server,
      * SILC network.
      */
     SILC_LOG_DEBUG(("New ID packet"));
-    silc_server_new_id(server, sock, packet);
-    break;
-
-  case SILC_PACKET_NEW_ID_LIST:
-    /*
-     * Received list of ID's. This packet is used by servers and routers
-     * to notify their primary router about clients and servers they have.
-     */
-    SILC_LOG_DEBUG(("New ID List packet"));
-    silc_server_new_id_list(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:
@@ -1676,6 +1951,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;
 
@@ -1686,6 +1963,8 @@ 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;
 
@@ -1695,81 +1974,68 @@ void silc_server_packet_parse_type(SilcServer server,
      * network are distributed using this packet.
      */
     SILC_LOG_DEBUG(("New Channel packet"));
-    silc_server_new_channel(server, sock, 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:
+  case SILC_PACKET_HEARTBEAT:
     /*
-     * Received new channel user packet. Information about new users on a
-     * channel are distributed between routers using this packet.  The
-     * router receiving this will redistribute it and also sent JOIN notify
-     * to local clients on the same channel. Normal server sends JOIN notify
-     * to its local clients on the channel.
+     * Received heartbeat.
      */
-    SILC_LOG_DEBUG(("New Channel User packet"));
-    silc_server_new_channel_user(server, sock, packet);
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     break;
 
-  case SILC_PACKET_NEW_CHANNEL_LIST:
+  case SILC_PACKET_KEY_AGREEMENT:
     /*
-     * List of new channel packets received. This is usually received when
-     * existing server or router connects to us and distributes information
-     * of all channels it has.
+     * Received heartbeat.
      */
-    SILC_LOG_DEBUG(("New Channel List packet"));
-    silc_server_new_channel_list(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_NEW_CHANNEL_USER_LIST:
+  case SILC_PACKET_REKEY:
     /*
-     * List of new channel user packets received. This is usually received
-     * when existing server or router connects to us and distributes 
-     * information of all channel users it has.
+     * Received re-key packet. The sender wants to regenerate the session
+     * keys.
      */
-    SILC_LOG_DEBUG(("New Channel User List packet"));
-    silc_server_new_channel_user_list(server, sock, packet);
+    SILC_LOG_DEBUG(("Re-key packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_rekey(server, sock, packet);
     break;
 
-  case SILC_PACKET_REPLACE_ID:
+  case SILC_PACKET_REKEY_DONE:
     /*
-     * 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.
+     * The re-key is done.
      */
-    SILC_LOG_DEBUG(("Replace ID packet"));
-    silc_server_replace_id(server, sock, packet);
-    break;
+    SILC_LOG_DEBUG(("Re-key done packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-  case SILC_PACKET_REMOVE_ID:
-    /*
-     * Received remove ID Packet. 
-     */
-    SILC_LOG_DEBUG(("Remove ID packet"));
-    silc_server_remove_id(server, sock, packet);
-    break;
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
 
-  case SILC_PACKET_REMOVE_CHANNEL_USER:
-    /*
-     * Received packet to remove user from a channel. Routers notify other
-     * routers about a user leaving a channel.
-     */
-    SILC_LOG_DEBUG(("Remove Channel User packet"));
-    silc_server_remove_channel_user(server, sock, packet);
-    break;
+      SilcServerRekeyInternalContext *proto_ctx = 
+       (SilcServerRekeyInternalContext *)sock->protocol->context;
 
-  case SILC_PACKET_SET_MODE:
-    /*
-     * Received packet to set the mode of channel or client's channel mode.
-     */
-    SILC_LOG_DEBUG(("Set Mode packet"));
-    silc_server_set_mode(server, sock, packet);
-    break;
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
 
-  case SILC_PACKET_HEARTBEAT:
-    /*
-     * Received heartbeat.
-     */
-    SILC_LOG_DEBUG(("Heartbeat 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 {
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
+    }
     break;
 
   default:
@@ -1779,12 +2045,41 @@ void silc_server_packet_parse_type(SilcServer server,
   
 }
 
+/* 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);
+}
+
+SILC_TASK_CALLBACK(silc_server_close_connection_final)
+{
+  silc_socket_free((SilcSocketConnection)context);
+}
+
 /* Closes connection to socket connection */
 
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
-  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
+  SILC_LOG_INFO(("Closing connection %s:%d [%s] (%d)", sock->hostname,
+                sock->port,  
+                (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                 sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                 sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                 "Router"), sock->sock));
 
   /* We won't listen for this connection anymore */
   silc_schedule_unset_listen_fd(sock->sock);
@@ -1796,7 +2091,11 @@ void silc_server_close_connection(SilcServer server,
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
   server->sockets[sock->sock] = NULL;
-  silc_socket_free(sock);
+
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_close_connection_final,
+                    (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
+                    SILC_TASK_PRI_NORMAL);
 }
 
 /* Sends disconnect message to remote connection and disconnects the 
@@ -1819,7 +2118,7 @@ void silc_server_disconnect_remote(SilcServer server,
 
   SILC_LOG_DEBUG(("Disconnecting remote host"));
 
-  SILC_LOG_INFO(("Disconnecting %s:%d (%s) [%s]", sock->hostname,
+  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" :
@@ -1836,8 +2135,80 @@ void silc_server_disconnect_remote(SilcServer server,
   silc_server_close_connection(server, sock);
 }
 
-/* Free's user_data pointer from socket connection object. This also sends
-   appropriate notify packets to the network to inform about leaving
+typedef struct {
+  SilcServer server;
+  SilcClientEntry client;
+} *FreeClientInternal;
+
+SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
+{
+  FreeClientInternal i = (FreeClientInternal)context;
+
+  silc_idlist_del_data(i->client);
+  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
+  silc_free(i);
+}
+
+/* Frees client data and notifies about client's signoff. */
+
+void silc_server_free_client_data(SilcServer server, 
+                                 SilcSocketConnection sock,
+                                 SilcClientEntry client, 
+                                 int notify,
+                                 char *signoff)
+{
+  FreeClientInternal i = silc_calloc(1, sizeof(*i));
+
+  /* If there is pending outgoing data for the client then purge it
+     to the network before removing the client entry. */
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    server->stat.packets_sent++;
+
+    if (sock->outbuf->data - sock->outbuf->head)
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
+
+  /* Send SIGNOFF notify to routers. */
+  if (notify && !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_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--;
+}
+
+/* Frees user_data pointer from socket connection object. This also sends
+   appropriate notify packets to the network to inform about leaving
    entities. */
 
 void silc_server_free_sock_user_data(SilcServer server, 
@@ -1849,26 +2220,7 @@ void silc_server_free_sock_user_data(SilcServer server,
   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);
-
-      /* XXX must take some info to history before freeing */
-
-      /* Send REMOVE_ID packet to routers. */
-      if (!server->standalone && server->router)
-       silc_server_send_remove_id(server, server->router->connection,
-                                  server->server_type == SILC_SERVER ?
-                                  FALSE : TRUE, user_data->id, 
-                                  SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
-
-      /* Free the client entry and everything in it */
-      silc_idlist_del_data(user_data);
-      silc_idlist_del_client(server->local_list, user_data);
-      server->stat.my_clients--;
-      server->stat.clients--;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.cell_clients--;
+      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
@@ -1876,20 +2228,14 @@ void silc_server_free_sock_user_data(SilcServer server,
     {
       SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
 
-      /* Send REMOVE_ID packet to routers. */
-      if (!server->standalone && server->router)
-       silc_server_send_remove_id(server, server->router->connection,
-                                  server->server_type == SILC_SERVER ?
-                                  FALSE : TRUE, user_data->id, 
-                                  SILC_ID_SERVER_LEN, SILC_ID_SERVER);
-
-      /* Then also free all client entries that this server owns as
-        they will become invalid now as well. */
-      silc_server_remove_clients_by_server(server, user_data);
+      /* 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->server_type == SILC_SERVER && server->router == user_data) {
+      if (server->router == user_data) {
        server->id_entry->router = NULL;
        server->router = NULL;
        server->standalone = TRUE;
@@ -1919,14 +2265,37 @@ void silc_server_free_sock_user_data(SilcServer server,
 
 /* 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'. */
+   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)
+                                        SilcServerEntry entry,
+                                        int server_signoff)
 {
   SilcIDCacheList list = NULL;
   SilcIDCacheEntry id_cache = NULL;
   SilcClientEntry client = NULL;
+  SilcBuffer idp;
+  SilcClientEntry *clients = NULL;
+  uint32 clients_c = 0;
+  unsigned char **argv = NULL;
+  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  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 (silc_idcache_find_by_id(server->local_list->clients, 
                              SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
@@ -1934,16 +2303,45 @@ int silc_server_remove_clients_by_server(SilcServer server,
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
-       
+       if (client->data.registered == FALSE) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
        if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
          if (!silc_idcache_list_next(list, &id_cache))
            break;
          else
            continue;
        }
 
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client);
+       silc_server_remove_from_channels(server, NULL, client, FALSE, 
+                                        NULL, FALSE);
        silc_idlist_del_client(server->local_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -1959,16 +2357,45 @@ int silc_server_remove_clients_by_server(SilcServer server,
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
+       if (client->data.registered == FALSE) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
        
        if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
          if (!silc_idcache_list_next(list, &id_cache))
            break;
          else
            continue;
        }
 
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client);
+       silc_server_remove_from_channels(server, NULL, client, FALSE,
+                                        NULL, FALSE);
        silc_idlist_del_client(server->global_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -1977,7 +2404,40 @@ int silc_server_remove_clients_by_server(SilcServer server,
     }
     silc_idcache_list_free(list);
   }
-  
+
+  /* Send the SERVER_SIGNOFF notify */
+  if (server_signoff) {
+    SilcBuffer args;
+
+    /* Send SERVER_SIGNOFF notify to our primary router */
+    if (!server->standalone && server->router) {
+      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);
+    }
+
+    args = silc_argument_payload_encode(argc, argv, argv_lens,
+                                       argv_types);
+    /* Send to local clients */
+    for (i = 0; i < clients_c; i++) {
+      silc_server_send_notify_args(server, clients[i]->connection,
+                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                                  argc, args);
+    }
+
+    silc_free(clients);
+    silc_buffer_free(args);
+    silc_free(argv);
+    silc_free(argv_lens);
+    silc_free(argv_types);
+  }
+
   return TRUE;
 }
 
@@ -2019,7 +2479,10 @@ int silc_server_channel_has_local(SilcChannelEntry channel)
 
 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;
@@ -2044,9 +2507,28 @@ void silc_server_remove_from_channels(SilcServer server,
     /* 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);
-      server->stat.my_channels--;
       continue;
     }
 
@@ -2055,15 +2537,42 @@ void silc_server_remove_from_channels(SilcServer server,
     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 (channel->global_users)
+      if (notify && channel->global_users)
        silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                          SILC_NOTIFY_TYPE_SIGNOFF, 1,
-                                          clidp->data, clidp->len);
+                                          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);
@@ -2073,9 +2582,24 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* Send notify to channel about client leaving SILC and thus
        the entire channel. */
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_SIGNOFF, 1,
-                                      clidp->data, clidp->len);
+    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);
@@ -2116,6 +2640,8 @@ int silc_server_remove_from_one_channel(SilcServer server,
     /* 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);
@@ -2144,9 +2670,28 @@ int silc_server_remove_from_one_channel(SilcServer server,
                                           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);
-      silc_buffer_free(clidp);
       server->stat.my_channels--;
       return FALSE;
     }
@@ -2212,20 +2757,31 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
 SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
                                                SilcServerID *router_id,
                                                char *cipher, 
+                                               char *hmac,
                                                char *channel_name,
                                                int broadcast)
 {
   SilcChannelID *channel_id;
   SilcChannelEntry entry;
   SilcCipher key;
+  SilcHmac newhmac;
 
   SILC_LOG_DEBUG(("Creating new channel"));
 
   if (!cipher)
-    cipher = "twofish";
+    cipher = "aes-256-cbc";
+  if (!hmac)
+    hmac = "hmac-sha1-96";
 
   /* Allocate cipher */
-  silc_cipher_alloc(cipher, &key);
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
+
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
+  }
 
   channel_name = strdup(channel_name);
 
@@ -2233,21 +2789,25 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   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);
+                                 NULL, key, newhmac);
   if (!entry) {
     silc_free(channel_name);
     return NULL;
   }
 
+  entry->cipher = strdup(cipher);
+  entry->hmac_name = strdup(hmac);
+
   /* Now create the actual key material */
-  silc_server_create_channel_key(server, entry, 16);
+  silc_server_create_channel_key(server, entry, 
+                                silc_cipher_get_key_len(key) / 8);
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE) {
+  if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
-  }
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
 
   server->stat.my_channels++;
 
@@ -2259,75 +2819,111 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 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 = "twofish";
+    cipher = "aes-256-cbc";
+  if (!hmac)
+    hmac = "hmac-sha1-96";
 
   /* Allocate cipher */
-  silc_cipher_alloc(cipher, &key);
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
+
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
+  }
 
   channel_name = strdup(channel_name);
 
   /* Create the channel */
   entry = silc_idlist_add_channel(server->local_list, channel_name, 
                                  SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key);
+                                 NULL, key, newhmac);
   if (!entry) {
     silc_free(channel_name);
     return NULL;
   }
 
   /* Now create the actual key material */
-  silc_server_create_channel_key(server, entry, 16);
+  silc_server_create_channel_key(server, entry, 
+                                silc_cipher_get_key_len(key) / 8);
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE) {
+  if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
-  }
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
 
   server->stat.my_channels++;
 
   return entry;
 }
 
+/* Channel's key re-key timeout callback. */
+
+SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
+{
+  SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
+  SilcServer server = (SilcServer)rekey->context;
+
+  silc_server_create_channel_key(server, rekey->channel, rekey->key_len);
+  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
+
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_channel_key_rekey,
+                    (void *)rekey, 3600, 0,
+                    SILC_TASK_TIMEOUT,
+                    SILC_TASK_PRI_NORMAL);
+}
+
 /* 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,
-                                   unsigned int key_len)
+                                   uint32 key_len)
 {
   int i;
-  unsigned char channel_key[32];
-  unsigned int len;
+  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;
+  }
 
   if (!channel->channel_key)
-    silc_cipher_alloc("twofish", &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 = sizeof(channel_key);
+    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 */
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       channel_key, len);
+  silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
 
   /* Remove old key if exists */
   if (channel->key) {
@@ -2340,6 +2936,29 @@ void silc_server_create_channel_key(SilcServer server,
   channel->key = silc_calloc(len, sizeof(*channel->key));
   memcpy(channel->key, channel_key, len);
   memset(channel_key, 0, sizeof(channel_key));
+
+  /* Generate HMAC key from the channel key data and set it */
+  if (!channel->hmac)
+    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+  silc_hash_make(channel->hmac->hash, channel->key, len, hash);
+  silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+  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);
+  }
 }
 
 /* Saves the channel key found in the encoded `key_payload' buffer. This 
@@ -2352,10 +2971,12 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 {
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
-  unsigned char *tmp;
-  unsigned int tmp_len;
+  unsigned char *tmp, hash[32];
+  uint32 tmp_len;
   char *cipher;
 
+  SILC_LOG_DEBUG(("Start"));
+
   /* Decode channel key payload */
   payload = silc_channel_key_payload_parse(key_payload);
   if (!payload) {
@@ -2391,7 +3012,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     goto out;
   }
 
-  cipher = silc_channel_key_get_cipher(payload, NULL);;
+  cipher = silc_channel_key_get_cipher(payload, NULL);
   if (!cipher) {
     channel = NULL;
     goto out;
@@ -2410,12 +3031,39 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     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);
+
+  /* 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 (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:
   if (id)
@@ -2502,7 +3150,7 @@ void silc_server_announce_servers(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_ID_LIST, 0,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
                            servers->data, servers->len, TRUE);
 
     silc_buffer_free(servers);
@@ -2572,30 +3220,103 @@ void silc_server_announce_clients(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_ID_LIST, 0,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
                            clients->data, clients->len, TRUE);
 
     silc_buffer_free(clients);
   }
 }
 
+static SilcBuffer 
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+{
+  va_list ap;
+
+  va_start(ap, argc);
+  return silc_notify_payload_encode(notify, argc, ap);
+}
+
+/* Returns assembled packets for channel users of the `channel'. */
+
+void silc_server_announce_get_channel_users(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *channel_users,
+                                           SilcBuffer *channel_users_modes)
+{
+  SilcChannelClientEntry chl;
+  SilcBuffer chidp, clidp;
+  SilcBuffer tmp;
+  int len;
+  unsigned char mode[4];
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Now find all users on the channel */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  silc_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);
+
+    /* JOIN Notify */
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2, 
+                                            clidp->data, clidp->len,
+                                            chidp->data, chidp->len);
+    len = tmp->len;
+    *channel_users = 
+      silc_buffer_realloc(*channel_users, 
+                         (*channel_users ? 
+                          (*channel_users)->truelen + len : len));
+    silc_buffer_pull_tail(*channel_users, 
+                         ((*channel_users)->end - 
+                          (*channel_users)->data));
+    
+    silc_buffer_put(*channel_users, tmp->data, tmp->len);
+    silc_buffer_pull(*channel_users, len);
+    silc_buffer_free(tmp);
+
+    /* CUMODE notify for mode change on the channel */
+    SILC_PUT32_MSB(chl->mode, mode);
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE, 
+                                            3, clidp->data, clidp->len,
+                                            mode, 4,
+                                            clidp->data, clidp->len);
+    len = tmp->len;
+    *channel_users_modes = 
+      silc_buffer_realloc(*channel_users_modes, 
+                         (*channel_users_modes ? 
+                          (*channel_users_modes)->truelen + len : len));
+    silc_buffer_pull_tail(*channel_users_modes, 
+                         ((*channel_users_modes)->end - 
+                          (*channel_users_modes)->data));
+    
+    silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
+    silc_buffer_pull(*channel_users_modes, len);
+    silc_buffer_free(tmp);
+
+    silc_buffer_free(clidp);
+  }
+  silc_buffer_free(chidp);
+}
+
 /* Returns assembled packets for all channels and users on those channels
    from the given ID List. The packets are in the form dictated by the
    New Channel and New Channel User payloads. */
 
-static void silc_server_announce_get_channels(SilcServer server,
-                                             SilcIDList id_list,
-                                             SilcBuffer *channels,
-                                             SilcBuffer *channel_users)
+void silc_server_announce_get_channels(SilcServer server,
+                                      SilcIDList id_list,
+                                      SilcBuffer *channels,
+                                      SilcBuffer *channel_users,
+                                      SilcBuffer *channel_users_modes)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
   unsigned char *cid;
-  unsigned short name_len;
+  uint16 name_len;
   int len;
 
+  SILC_LOG_DEBUG(("Start"));
+
   /* Go through all channels in the list */
   if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY, 
                              SILC_ID_CHANNEL, &list)) {
@@ -2606,7 +3327,7 @@ static void silc_server_announce_get_channels(SilcServer server,
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
-       len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+       len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
        *channels = 
          silc_buffer_realloc(*channels, 
                              (*channels ? (*channels)->truelen + len : len));
@@ -2618,33 +3339,13 @@ static void silc_server_announce_get_channels(SilcServer server,
                                                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);
 
-       /* Now find all users on the channel */
-       silc_list_start(channel->user_list);
-       while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-         unsigned char *clid;
-
-         clid = silc_id_id2str(chl->client->id, SILC_ID_CLIENT);
-         
-         len = 4 + SILC_ID_CHANNEL_LEN + SILC_ID_CLIENT_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_format(*channel_users,
-                            SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                            SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
-                            SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
-                            SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
-                            SILC_STR_END);
-         silc_buffer_pull(*channel_users, len);
-         silc_free(clid);
-       }
+       silc_server_announce_get_channel_users(server, channel,
+                                              channel_users,
+                                              channel_users_modes);
 
        silc_free(cid);
 
@@ -2663,17 +3364,19 @@ static void silc_server_announce_get_channels(SilcServer server,
 
 void silc_server_announce_channels(SilcServer server)
 {
-  SilcBuffer channels = NULL, channel_users = NULL;
+  SilcBuffer channels = NULL, channel_users = NULL, channel_users_modes = NULL;
 
   SILC_LOG_DEBUG(("Announcing channels and channel users"));
 
   /* Get channels and channel users in local list */
   silc_server_announce_get_channels(server, server->local_list,
-                                   &channels, &channel_users);
+                                   &channels, &channel_users,
+                                   &channel_users_modes);
 
   /* Get channels and channel users in global list */
   silc_server_announce_get_channels(server, server->global_list,
-                                   &channels, &channel_users);
+                                   &channels, &channel_users,
+                                   &channel_users_modes);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
@@ -2681,7 +3384,7 @@ void silc_server_announce_channels(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_CHANNEL_LIST, 0,
+                           SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
                            channels->data, channels->len,
                            FALSE);
 
@@ -2695,10 +3398,411 @@ void silc_server_announce_channels(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_CHANNEL_USER_LIST, 0,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
                            channel_users->data, channel_users->len,
                            FALSE);
 
     silc_buffer_free(channel_users);
   }
+
+  if (channel_users_modes) {
+    silc_buffer_push(channel_users_modes, 
+                    channel_users_modes->data - channel_users_modes->head);
+    SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes->data, 
+                    channel_users_modes->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           channel_users_modes->data, 
+                           channel_users_modes->len,
+                           FALSE);
+
+    silc_buffer_free(channel_users_modes);
+  }
+}
+
+/* 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). */
+
+SILC_TASK_CALLBACK(silc_server_failure_callback)
+{
+  SilcServerFailureContext f = (SilcServerFailureContext)context;
+
+  if (f->sock->protocol) {
+    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+    f->sock->protocol->execute(f->server->timeout_queue, 0,
+                              f->sock->protocol, f->sock->sock, 0, 0);
+  }
+
+  silc_free(f);
+}
+
+/* Assembles user list and users mode list from the `channel'. */
+
+void silc_server_get_users_on_channel(SilcServer server,
+                                     SilcChannelEntry channel,
+                                     SilcBuffer *user_list,
+                                     SilcBuffer *mode_list,
+                                     uint32 *user_count)
+{
+  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);
+
+    /* Client's mode on channel */
+    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
+    silc_buffer_pull(client_mode_list, 4);
+
+    list_count++;
+  }
+  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;
+}
+
+/* Saves users and their modes to the `channel'. */
+
+void silc_server_save_users_on_channel(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcChannelEntry channel,
+                                      SilcClientID *noadd,
+                                      SilcBuffer user_list,
+                                      SilcBuffer mode_list,
+                                      uint32 user_count)
+{
+  int i;
+
+  /* Cache the received Client ID's and modes. This cache expires
+     whenever server sends notify message to channel. It means two things;
+     some user has joined or leaved the channel. XXX TODO! */
+  for (i = 0; i < user_count; i++) {
+    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;
+
+    /* Mode */
+    SILC_GET32_MSB(mode, mode_list->data);
+    silc_buffer_pull(mode_list, 4);
+
+    if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+      silc_free(client_id);
+      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;
+      }
+
+      /* 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;
+      }
+
+      client->data.registered = TRUE;
+    }
+
+    silc_free(client_id);
+
+    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);
+    }
+  }
+}
+
+/* Lookups route to the client indicated by the `id_data'. The connection
+   object and internal data object is returned. Returns NULL if route
+   could not be found to the client. If the `client_id' is specified then
+   it is used and the `id_data' is ignored. */
+
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+                                                 unsigned char *id_data,
+                                                 uint32 id_len,
+                                                 SilcClientID *client_id,
+                                                 SilcIDListData *idata)
+{
+  SilcClientID *id;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* 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);
+  }
+
+  /* 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);
+
+    if (client && client->data.registered == FALSE)
+      return NULL;
+
+    /* If we are router and the client has router then the client is in
+       our cell but not directly connected to us. */
+    if (server->server_type == SILC_ROUTER && client->router) {
+      /* We are of course in this case the client's router thus the real
+        "router" of the client is the server who owns the client. Thus
+        we will send the packet to that server. */
+      if (idata)
+       *idata = (SilcIDListData)client->router;
+      return client->router->connection;
+    }
+
+    /* Seems that client really is directly connected to us */
+    if (idata)
+      *idata = (SilcIDListData)client;
+    return client->connection;
+  }
+
+  /* Destination belongs to someone not in this server. If we are normal
+     server our action is to send the packet to our router. */
+  if (server->server_type == SILC_SERVER && !server->standalone) {
+    silc_free(id);
+    if (idata)
+      *idata = (SilcIDListData)server->router;
+    return server->router->connection;
+  }
+
+  /* We are router and we will perform route lookup for the destination 
+     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;
+
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+
+      silc_free(id);
+      if (idata)
+       *idata = (SilcIDListData)dst_sock->user_data;
+      return dst_sock;
+    }
+  }
+
+  silc_free(id);
+  return NULL;
+}
+
+/* Encodes and returns channel list of channels the `client' has joined.
+   Secret channels are not put to the list. */
+
+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;
+
+  silc_list_start(client->channels);
+  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+    channel = chl->channel;
+
+    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);
+  }
+
+  if (buffer)
+    silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
+/* Finds client entry by Client ID and if it is not found then resolves
+   it using WHOIS command. */
+
+SilcClientEntry silc_server_get_client_resolve(SilcServer server,
+                                              SilcClientID *client_id)
+{
+  SilcClientEntry client;
+
+  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client && server->server_type == SILC_ROUTER)
+      return NULL;
+  }
+
+  if (!client && server->standalone)
+    return NULL;
+
+  if (!client || !client->nickname || !client->username) {
+    SilcBuffer buffer, idp;
+
+    idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
+                                           ++server->cmd_ident, 1,
+                                           3, idp->data, idp->len);
+    silc_server_packet_send(server, client ? client->router->connection :
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, 0,
+                           buffer->data, buffer->len, FALSE);
+    silc_buffer_free(idp);
+    silc_buffer_free(buffer);
+  }
+
+  return client;
+}
+
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_callback)
+{
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcServer server = (SilcServer)idata->rekey->context;
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = FALSE;
+  proto_ctx->pfs = idata->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+                     &protocol, proto_ctx, silc_server_rekey_final);
+  sock->protocol = protocol;
+      
+  /* Run the protocol */
+  protocol->execute(server->timeout_queue, 0, protocol, 
+                   sock->sock, 0, 0);
+
+  /* Re-register re-key timeout */
+  silc_task_register(server->timeout_queue, sock->sock, 
+                    silc_server_rekey_callback,
+                    context, idata->rekey->timeout, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx =
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+  SilcSocketConnection sock = ctx->sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    silc_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;
+  }
+
+  /* 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);
 }