updates.
[silc.git] / apps / silcd / server.c
index 6962409d84774e37b7a4e484e2105fe4ba5386c6..f3d00f16cf7d48dbf1d9777a529cf11331b9cbff 100644 (file)
@@ -2,7 +2,7 @@
 
   server.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
@@ -121,12 +121,14 @@ int silc_server_init(SilcServer server)
   assert(server->config);
 
   /* Set public and private keys */
-  server->public_key = server->config->server_keys->public_key;
-  server->private_key = server->config->server_keys->private_key;
-  if (!server->public_key || !server->private_key) {
+  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 */
@@ -143,10 +145,14 @@ int silc_server_init(SilcServer server)
   silc_server_config_setlogfiles(server->config);
  
   /* Register all configured ciphers, PKCS and hash functions. */
-  silc_server_config_register_ciphers(server->config);
-  silc_server_config_register_pkcs(server->config);
-  silc_server_config_register_hashfuncs(server->config);
-  silc_server_config_register_hmacs(server->config);
+  if (!silc_server_config_register_ciphers(server->config))
+    silc_cipher_register_default();
+  if (!silc_server_config_register_pkcs(server->config))
+    silc_pkcs_register_default();
+  if (!silc_server_config_register_hashfuncs(server->config))
+    silc_hash_register_default();
+  if (!silc_server_config_register_hmacs(server->config))
+    silc_hmac_register_default();
 
   /* Initialize random number generator for the server. */
   server->rng = silc_rng_alloc();
@@ -180,17 +186,17 @@ int silc_server_init(SilcServer server)
 
   /* Initialize ID caches */
   server->local_list->clients = 
-    silc_idcache_alloc(0, silc_idlist_client_destructor);
-  server->local_list->servers = silc_idcache_alloc(0, NULL);
-  server->local_list->channels = silc_idcache_alloc(0, NULL);
+    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
+  server->local_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
+  server->local_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 
   /* These are allocated for normal server as well as these hold some 
      global information that the server has fetched from its router. For 
      router these are used as they are supposed to be used on router. */
   server->global_list->clients = 
-    silc_idcache_alloc(0, silc_idlist_client_destructor);
-  server->global_list->servers = silc_idcache_alloc(0, NULL);
-  server->global_list->channels = silc_idcache_alloc(0, NULL);
+    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
+  server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
+  server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 
   /* Allocate the entire socket list that is used in server. Eventually 
      all connections will have entry in this table (it is a table of 
@@ -214,7 +220,7 @@ int silc_server_init(SilcServer server)
     
     server->id = id;
     server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
-    server->id_string_len = silc_id_get_len(SILC_ID_SERVER);
+    server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
     server->id_type = SILC_ID_SERVER;
     server->server_name = server->config->server_info->server_name;
 
@@ -237,6 +243,23 @@ int silc_server_init(SilcServer server)
                      &newsocket);
 
     server->sockets[sock[i]] = newsocket;
+    
+    /* Perform name and address lookups to resolve the listenning address
+       and port. */
+    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
+                                    &newsocket->ip)) {
+      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+         !newsocket->ip) {
+       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+                       newsocket->hostname ? newsocket->hostname :
+                       newsocket->ip ? newsocket->ip : ""));
+       server->stat.conn_failures++;
+       goto err0;
+      }
+      if (!newsocket->hostname)
+       newsocket->hostname = strdup(newsocket->ip);
+    }
+    newsocket->port = silc_net_get_local_port(sock[i]);
 
     /* Put the allocated socket pointer also to the entry allocated above 
        for fast back-referencing to the socket list. */
@@ -244,48 +267,32 @@ int silc_server_init(SilcServer server)
     server->id_entry = id_entry;
   }
 
-  /* Register the task queues. In SILC we have by default three task queues. 
-     One task queue for non-timeout tasks which perform different kind of 
-     I/O on file descriptors, timeout task queue for timeout tasks, and,
-     generic non-timeout task queue whose tasks apply to all connections. */
-  silc_task_queue_alloc(&server->io_queue, TRUE);
-  if (!server->io_queue) {
-    goto err0;
-  }
-  silc_task_queue_alloc(&server->timeout_queue, TRUE);
-  if (!server->timeout_queue) {
-    goto err1;
-  }
-  silc_task_queue_alloc(&server->generic_queue, TRUE);
-  if (!server->generic_queue) {
-    goto err1;
-  }
-
   /* Register protocols */
   silc_server_protocols_register();
 
-  /* Initialize the scheduler */
-  silc_schedule_init(&server->io_queue, &server->timeout_queue, 
-                    &server->generic_queue, 
-                    SILC_SERVER_MAX_CONNECTIONS);
+  /* Initialize the scheduler. */
+  server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
+  if (!server->schedule)
+    goto err0;
   
-  /* Add the first task to the queue. This is task that is executed by
+  /* Add the first task to the scheduler. This is task that is executed by
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  silc_task_register(server->timeout_queue, sock[0], 
-                    silc_server_connect_to_router,
-                    (void *)server, 0, 1,
-                    SILC_TASK_TIMEOUT,
-                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(server->schedule, sock[0], 
+                        silc_server_connect_to_router,
+                        (void *)server, 0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 
-  /* Add listener task to the queue. This task receives new connections to the 
-     server. This task remains on the queue until the end of the program. */
-  silc_task_register(server->io_queue, sock[0],
-                    silc_server_accept_new_connection,
-                    (void *)server, 0, 0, 
-                    SILC_TASK_FD,
-                    SILC_TASK_PRI_NORMAL);
+  /* Add listener task to the scheduler. This task receives new connections
+     to the server. This task remains on the queue until the end of the 
+     program. */
+  silc_schedule_task_add(server->schedule, sock[0],
+                        silc_server_accept_new_connection,
+                        (void *)server, 0, 0, 
+                        SILC_TASK_FD,
+                        SILC_TASK_PRI_NORMAL);
   server->listenning = TRUE;
 
   /* If server connections has been configured then we must be router as
@@ -299,29 +306,26 @@ int silc_server_init(SilcServer server)
   /* Clients local list */
   purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->local_list->clients;
-  purge->timeout_queue = server->timeout_queue;
-  silc_task_register(purge->timeout_queue, 0, 
-                    silc_idlist_purge,
-                    (void *)purge, 600, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  purge->schedule = server->schedule;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, 600, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   /* Clients global list */
   purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->global_list->clients;
-  purge->timeout_queue = server->timeout_queue;
-  silc_task_register(purge->timeout_queue, 0, 
-                    silc_idlist_purge,
-                    (void *)purge, 300, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  purge->schedule = server->schedule;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, 300, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
   return TRUE;
 
-  silc_task_queue_free(server->timeout_queue);
- err1:
-  silc_task_queue_free(server->io_queue);
  err0:
   for (i = 0; i < sock_count; i++)
     silc_net_close_server(sock[i]);
@@ -441,11 +445,8 @@ void silc_server_stop(SilcServer server)
 {
   SILC_LOG_DEBUG(("Stopping server"));
 
-  /* Stop the scheduler, although it might be already stopped. This
-     doesn't hurt anyone. This removes all the tasks and task queues,
-     as well. */
-  silc_schedule_stop();
-  silc_schedule_uninit();
+  silc_schedule_stop(server->schedule);
+  silc_schedule_uninit(server->schedule);
 
   silc_server_protocols_unregister();
 
@@ -460,9 +461,11 @@ void silc_server_run(SilcServer server)
 {
   SILC_LOG_DEBUG(("Running server"));
 
+  SILC_LOG_INFO(("SILC Server started"));
+
   /* Start the scheduler, the heart of the SILC server. When this returns
      the program will be terminated. */
-  silc_schedule();
+  silc_schedule(server->schedule);
 }
 
 /* Timeout callback that will be called to retry connecting to remote
@@ -497,10 +500,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
   }
 
   /* Wait one before retrying */
-  silc_task_register(server->timeout_queue, fd, silc_server_connect_router,
-                    context, sconn->retry_timeout, 
-                    server->params->retry_interval_min_usec,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(server->schedule, fd, silc_server_connect_router,
+                        context, sconn->retry_timeout, 
+                        server->params->retry_interval_min_usec,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* Generic routine to use connect to a router. */
@@ -522,10 +525,10 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
                                    sconn->remote_host);
   if (sock < 0) {
     SILC_LOG_ERROR(("Could not connect to router"));
-    silc_task_register(server->timeout_queue, fd, 
-                      silc_server_connect_to_router_retry,
-                      context, 0, 1, SILC_TASK_TIMEOUT, 
-                      SILC_TASK_PRI_NORMAL);
+    silc_schedule_task_add(server->schedule, fd, 
+                          silc_server_connect_to_router_retry,
+                          context, 0, 1, SILC_TASK_TIMEOUT, 
+                          SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -563,7 +566,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   /* Register a timeout task that will be executed if the protocol
      is not executed within set limit. */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock, 
+    silc_schedule_task_add(server->schedule, sock, 
                       silc_server_timeout_remote,
                       server, server->params->protocol_timeout,
                       server->params->protocol_timeout_usec,
@@ -580,7 +583,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   SILC_REGISTER_CONNECTION_FOR_IO(sock);
   
   /* Run the protocol */
-  protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+  silc_protocol_execute(protocol, server->schedule, 0, 0);
 }
   
 /* This function connects to our primary router or if we are a router this
@@ -609,7 +612,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       sconn->remote_host = strdup(server->config->routers->host);
       sconn->remote_port = server->config->routers->port;
 
-      silc_task_register(server->timeout_queue, fd, 
+      silc_schedule_task_add(server->schedule, fd, 
                         silc_server_connect_router,
                         (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
                         SILC_TASK_PRI_NORMAL);
@@ -639,7 +642,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
        sconn->remote_host = strdup(ptr->host);
        sconn->remote_port = ptr->port;
 
-       silc_task_register(server->timeout_queue, fd, 
+       silc_schedule_task_add(server->schedule, fd, 
                           silc_server_connect_router,
                           (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
                           SILC_TASK_PRI_NORMAL);
@@ -669,7 +672,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
-  SilcSocketConnection sock = server->sockets[fd];
+  SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
   SilcServerConfigSectionServerConnection *conn = NULL;
 
@@ -688,7 +691,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
+    silc_schedule_task_del_by_callback(server->schedule,
                                     silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
@@ -715,7 +718,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
+    silc_schedule_task_del_by_callback(server->schedule,
                                     silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
@@ -757,7 +760,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
+    silc_schedule_task_del_by_callback(server->schedule,
                                     silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
@@ -783,15 +786,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
      this timelimit the connection will be terminated. Currently
      this is 15 seconds and is hard coded limit (XXX). */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock->sock, 
+    silc_schedule_task_add(server->schedule, sock->sock, 
                       silc_server_timeout_remote,
                       (void *)server, 15, 0,
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_LOW);
 
   /* Run the protocol */
-  sock->protocol->execute(server->timeout_queue, 0, 
-                         sock->protocol, sock->sock, 0, 0);
+  silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 }
 
 /* Finalizes the connection to router. Registers a server task to the
@@ -809,6 +811,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcBuffer packet;
   SilcServerHBContext hb_context;
   unsigned char *id_string;
+  uint32 id_len;
   SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
@@ -826,23 +829,23 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   /* Add a task to the queue. This task receives new connections to the 
      server. This task remains on the queue until the end of the program. */
   if (!server->listenning) {
-    silc_task_register(server->io_queue, server->sock, 
-                      silc_server_accept_new_connection,
-                      (void *)server, 0, 0, 
-                      SILC_TASK_FD,
-                      SILC_TASK_PRI_NORMAL);
+    silc_schedule_task_add(server->schedule, server->sock, 
+                          silc_server_accept_new_connection,
+                          (void *)server, 0, 0, 
+                          SILC_TASK_FD,
+                          SILC_TASK_PRI_NORMAL);
     server->listenning = TRUE;
   }
 
   /* Send NEW_SERVER packet to the router. We will become registered
      to the SILC network after sending this packet. */
   id_string = silc_id_id2str(server->id, SILC_ID_SERVER);
-  packet = silc_buffer_alloc(2 + 2 + SILC_ID_SERVER_LEN + 
-                            strlen(server->server_name));
+  id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
+  packet = silc_buffer_alloc(2 + 2 + id_len + strlen(server->server_name));
   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
   silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(SILC_ID_SERVER_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_SERVER_LEN),
+                    SILC_STR_UI_SHORT(id_len),
+                    SILC_STR_UI_XNSTRING(id_string, id_len),
                     SILC_STR_UI_SHORT(strlen(server->server_name)),
                     SILC_STR_UI_XNSTRING(server->server_name,
                                          strlen(server->server_name)),
@@ -884,12 +887,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   hb_context->server = server;
   silc_socket_set_heartbeat(sock, 600, hb_context,
                            silc_server_perform_heartbeat,
-                           server->timeout_queue);
+                           server->schedule);
 
   /* Register re-key timeout */
   idata->rekey->timeout = 3600; /* XXX hardcoded */
   idata->rekey->context = (void *)server;
-  silc_task_register(server->timeout_queue, sock->sock, 
+  silc_schedule_task_add(server->schedule, sock->sock, 
                     silc_server_rekey_callback,
                     (void *)sock, idata->rekey->timeout, 0,
                     SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
@@ -919,72 +922,104 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   sock->protocol = NULL;
 }
 
-/* Accepts new connections to the server. Accepting new connections are
-   done in three parts to make it async. */
+/* Host lookup callbcak that is called after the incoming connection's
+   IP and FQDN lookup is performed. This will actually check the acceptance
+   of the incoming connection and will register the key exchange protocol
+   for this connection. */
 
-SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+static void 
+silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
+                                        void *context)
 {
   SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
   SilcServerKEInternalContext *proto_ctx;
-  int sock;
-
-  SILC_LOG_DEBUG(("Accepting new connection"));
+  void *cconfig, *sconfig, *rconfig;
+  SilcServerConfigSectionDenyConnection *deny;
+  int port;
 
-  server->stat.conn_attempts++;
+  SILC_LOG_DEBUG(("Start"));
 
-  sock = silc_net_accept_connection(server->sock);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+  /* Check whether we could resolve both IP and FQDN. */
+  if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
+                   server->params->require_reverse_mapping)) {
+    SILC_LOG_ERROR(("IP/DNS lookup failed %s",
+                   sock->hostname ? sock->hostname :
+                   sock->ip ? sock->ip : ""));
     server->stat.conn_failures++;
+    silc_server_disconnect_remote(server, sock,
+                                 "Server closed connection: Unknown host");
     return;
   }
 
-  /* Check max connections */
-  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    SILC_LOG_ERROR(("Refusing connection, server is full"));
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it
+     will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
+
+  SILC_LOG_INFO(("Incoming connection from %s (%s)", sock->hostname,
+                sock->ip));
+
+  port = server->sockets[server->sock]->port; /* Listenning port */
+
+  /* Check whether this connection is denied to connect to us. */
+  deny = silc_server_config_denied_conn(server->config, sock->ip, port);
+  if (!deny)
+    deny = silc_server_config_denied_conn(server->config, sock->hostname,
+                                         port);
+  if (deny) {
+    /* The connection is denied */
+    SILC_LOG_INFO(("Connection %s (%s) is denied", 
+                   sock->hostname, sock->ip));
+    silc_server_disconnect_remote(server, sock, deny->comment ?
+                                 deny->comment :
+                                 "Server closed connection: "
+                                 "Connection refused");
     server->stat.conn_failures++;
     return;
   }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* We don't create a ID yet, since we don't know what type of connection
-     this is yet. But, we do add the connection to the socket table. */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-  server->sockets[sock] = newsocket;
-
-  /* XXX This MUST be done async as this will block the entire server
-     process. Either we have to do our own resolver stuff or in the future
-     we can use threads. */
-  /* Perform name and address lookups for the remote host. */
-  if (!silc_net_check_host_by_sock(sock, &newsocket->hostname, 
-                                  &newsocket->ip)) {
-    if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-       !newsocket->ip) {
-      SILC_LOG_ERROR(("IP/DNS lookup failed %s",
-                     newsocket->hostname ? newsocket->hostname :
-                     newsocket->ip ? newsocket->ip : ""));
-      server->stat.conn_failures++;
-      return;
-    }
-    if (!newsocket->hostname)
-      newsocket->hostname = strdup(newsocket->ip);
+  /* Check whether we have configred this sort of connection at all. We
+     have to check all configurations since we don't know what type of
+     connection this is. */
+  if (!(cconfig = silc_server_config_find_client_conn(server->config,
+                                                     sock->ip, port)))
+    cconfig = silc_server_config_find_client_conn(server->config,
+                                                 sock->hostname, 
+                                                 port);
+  if (!(sconfig = silc_server_config_find_server_conn(server->config,
+                                                    sock->ip, 
+                                                    port)))
+    sconfig = silc_server_config_find_server_conn(server->config,
+                                                 sock->hostname,
+                                                 port);
+  if (!(rconfig = silc_server_config_find_router_conn(server->config,
+                                                    sock->ip, port)))
+    rconfig = silc_server_config_find_router_conn(server->config,
+                                                 sock->hostname, 
+                                                 port);
+  if (!cconfig && !sconfig && !rconfig) {
+    silc_server_disconnect_remote(server, sock, 
+                                 "Server closed connection: "
+                                 "Connection refused");
+    server->stat.conn_failures++;
+    return;
   }
-  newsocket->port = silc_net_get_remote_port(sock);
 
-  SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
-                newsocket->ip));
+  /* The connection is allowed */
 
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = context;
-  proto_ctx->sock = newsocket;
+  proto_ctx->sock = sock;
   proto_ctx->rng = server->rng;
   proto_ctx->responder = TRUE;
+  proto_ctx->cconfig = cconfig;
+  proto_ctx->sconfig = sconfig;
+  proto_ctx->rconfig = rconfig;
 
   /* Prepare the connection for key exchange protocol. We allocate the
      protocol but will not start it yet. The connector will be the
@@ -992,7 +1027,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      there before we start the protocol. */
   server->stat.auth_attempts++;
   silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &newsocket->protocol, proto_ctx, 
+                     &sock->protocol, proto_ctx, 
                      silc_server_accept_new_connection_second);
 
   /* Register a timeout task that will be executed if the connector
@@ -1000,19 +1035,55 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      now, this is a hard coded limit. After 60 secs the connection will
      be closed if the key exchange protocol has not been started. */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, newsocket->sock, 
-                      silc_server_timeout_remote,
-                      context, 60, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+    silc_schedule_task_add(server->schedule, sock->sock, 
+                          silc_server_timeout_remote,
+                          context, 60, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
+}
 
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it
-     will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  SILC_REGISTER_CONNECTION_FOR_IO(sock);
+/* Accepts new connections to the server. Accepting new connections are
+   done in three parts to make it async. */
+
+SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+{
+  SilcServer server = (SilcServer)context;
+  SilcSocketConnection newsocket;
+  int sock;
+
+  SILC_LOG_DEBUG(("Accepting new connection"));
+
+  server->stat.conn_attempts++;
+
+  sock = silc_net_accept_connection(server->sock);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* Check max connections */
+  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* We don't create a ID yet, since we don't know what type of connection
+     this is yet. But, we do add the connection to the socket table. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
+
+  /* Perform asynchronous host lookup. This will lookup the IP and the
+     FQDN of the remote connection. After the lookup is done the connection
+     is accepted further. */
+  silc_socket_host_lookup(newsocket, TRUE, 
+                         silc_server_accept_new_connection_lookup, context, 
+                         server->schedule);
 }
 
 /* Second part of accepting new connection. Key exchange protocol has been
@@ -1027,7 +1098,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = server->sockets[fd];
+  SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1045,7 +1116,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
+    silc_schedule_task_del_by_callback(server->schedule,
                                     silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
@@ -1073,7 +1144,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_task_unregister_by_callback(server->timeout_queue,
+    silc_schedule_task_del_by_callback(server->schedule,
                                     silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
@@ -1091,6 +1162,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   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);
@@ -1111,11 +1185,11 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
      this timelimit the connection will be terminated. Currently
      this is 60 seconds and is hard coded limit (XXX). */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock->sock, 
-                      silc_server_timeout_remote,
-                      (void *)server, 60, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+    silc_schedule_task_add(server->schedule, sock->sock, 
+                          silc_server_timeout_remote,
+                          (void *)server, 60, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
 }
 
 /* Final part of accepting new connection. The connection has now
@@ -1148,7 +1222,7 @@ 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_schedule_task_del_by_callback(server->schedule,
                                     silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
@@ -1156,8 +1230,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     return;
   }
 
-  sock->type = ctx->conn_type;
-  switch(sock->type) {
+  switch(ctx->conn_type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry client;
@@ -1170,11 +1243,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         and other information is created after we have received NEW_CLIENT
         packet from client. */
       client = silc_idlist_add_client(server->local_list, 
-                                     NULL, 0, NULL, NULL, NULL, NULL, sock);
+                                     NULL, NULL, NULL, NULL, NULL, sock);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
-       break;
+       silc_server_disconnect_remote(server, sock, 
+                                     "Server closed connection: "
+                                     "Authentication failed");
+       server->stat.auth_failures++;
+       goto out;
       }
 
       /* Statistics */
@@ -1190,12 +1267,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry new_server;
+      SilcServerConfigSectionServerConnection *conn = 
+       ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
+       ctx->sconfig : ctx->rconfig;
 
       SILC_LOG_DEBUG(("Remote host is %s", 
-                     sock->type == SILC_SOCKET_TYPE_SERVER ? 
+                     ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
                      "server" : "router"));
       SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
-                    sock->ip, sock->type == SILC_SOCKET_TYPE_SERVER ? 
+                    sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
                     "server" : "router"));
 
       /* Add the server into server cache. The server name and Server ID
@@ -1204,30 +1284,36 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         are router. */
       new_server = 
        silc_idlist_add_server(server->local_list, NULL,
-                              sock->type == SILC_SOCKET_TYPE_SERVER ?
+                              ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
                               SILC_SERVER : SILC_ROUTER, NULL, 
-                              sock->type == SILC_SOCKET_TYPE_SERVER ?
+                              ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
                               server->id_entry : NULL, sock);
       if (!new_server) {
        SILC_LOG_ERROR(("Could not add new server to cache"));
        silc_free(sock->user_data);
-       break;
+       silc_server_disconnect_remote(server, sock, 
+                                     "Server closed connection: "
+                                     "Authentication failed");
+       server->stat.auth_failures++;
+       goto out;
       }
 
       /* Statistics */
-      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER)
        server->stat.my_servers++;
       else
        server->stat.my_routers++;
       server->stat.servers++;
 
       id_entry = (void *)new_server;
-      
-      /* There is connection to other server now, if it is router then
-        we will have connection to outside world.  If we are router but
-        normal server connected to us then we will remain standalone,
-        if we are standlone. */
-      if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
+
+      /* Check whether this connection is to be our primary router connection
+        if we dont' already have the primary route. */
+      if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
+       if (silc_server_config_is_primary_route(server->config) &&
+           !conn->initiator)
+         break;
+
        SILC_LOG_DEBUG(("We are not standalone server anymore"));
        server->standalone = FALSE;
        if (!server->id_entry->router) {
@@ -1235,12 +1321,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          server->router = id_entry;
        }
       }
+
       break;
     }
   default:
     break;
   }
 
+  sock->type = ctx->conn_type;
+
   /* Add the common data structure to the ID entry. */
   if (id_entry)
     silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
@@ -1259,9 +1348,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   hb_context->server = server;
   silc_socket_set_heartbeat(sock, 600, hb_context,
                            silc_server_perform_heartbeat,
-                           server->timeout_queue);
+                           server->schedule);
 
-  silc_task_unregister_by_callback(server->timeout_queue,
+ out:
+  silc_schedule_task_del_by_callback(server->schedule,
                                   silc_server_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->packet)
@@ -1303,21 +1393,29 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (sock->outbuf->data - sock->outbuf->head)
      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    ret = silc_server_packet_send_real(server, sock, TRUE);
+    /* Send the packet */
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
 
-    if (ret == -1)
+    if (ret == -1) {
+      SILC_LOG_ERROR(("Error sending packet to connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
       return;
+    }
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
        available for this connection it will be set for output as well. 
        This call clears the output setting and sets it only for input. */
-    SILC_SET_CONNECTION_FOR_INPUT(fd);
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
     SILC_UNSET_OUTBUF_PENDING(sock);
 
     silc_buffer_clear(sock->outbuf);
@@ -1328,9 +1426,18 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
   /* Read some data from connection */
   ret = silc_packet_receive(sock);
-  if (ret < 0)
+  if (ret < 0) {
+
+    if (ret == -1)
+      SILC_LOG_ERROR(("Error receiving packet from connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     return;
-    
+  }    
+
   /* EOF */
   if (ret == 0) {
     SILC_LOG_DEBUG(("Read EOF"));
@@ -1351,11 +1458,11 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
        start re-connecting phase. */
     if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && 
        sock == server->router->connection)
-      silc_task_register(server->timeout_queue, 0, 
-                        silc_server_connect_to_router,
-                        context, 1, 0,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_connect_to_router,
+                            context, 1, 0,
+                            SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
 
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock);
@@ -1377,7 +1484,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
@@ -1419,7 +1526,7 @@ static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
 
   return FALSE;
 }
-
+  
 /* Parses whole packet, received earlier. */
 
 SILC_TASK_CALLBACK(silc_server_packet_parse_real)
@@ -1428,16 +1535,25 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   SilcServer server = (SilcServer)parse_ctx->context;
   SilcSocketConnection sock = parse_ctx->sock;
   SilcPacketContext *packet = parse_ctx->packet;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
   int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Decrypt the received packet */
-  ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac, 
+  ret = silc_packet_decrypt(idata ? idata->receive_key : NULL, 
+                           idata ? idata->hmac_receive : NULL, 
                            packet->buffer, packet,
                            silc_server_packet_decrypt_check, parse_ctx);
-  if (ret < 0)
+  if (ret < 0) {
+    SILC_LOG_WARNING(("Packet decryption failed for connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     goto out;
+  }
 
   if (ret == 0) {
     /* Parse the packet. Packet type is returned. */
@@ -1457,7 +1573,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     if (client && client->id) {
       void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
                                packet->src_id_type);
-      if (SILC_ID_CLIENT_COMPARE(client->id, id)) {
+      if (!id || !SILC_ID_CLIENT_COMPARE(client->id, id)) {
        silc_free(id);
        goto out;
       }
@@ -1468,9 +1584,10 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   if (server->server_type == SILC_ROUTER) {
     /* Route the packet if it is not destined to us. Other ID types but
        server are handled separately after processing them. */
-    if (packet->dst_id_type == SILC_ID_SERVER && 
+    if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
+       packet->dst_id_type == SILC_ID_SERVER && 
        sock->type != SILC_SOCKET_TYPE_CLIENT &&
-       SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+       memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
       
       /* Route the packet to fastest route for the destination ID */
       void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
@@ -1484,18 +1601,21 @@ 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_packet_context_free(packet);
@@ -1512,26 +1632,18 @@ void silc_server_packet_parse(SilcPacketParserContext *parser_context)
 
   switch (sock->type) {
   case SILC_SOCKET_TYPE_UNKNOWN:
+  case SILC_SOCKET_TYPE_CLIENT:
     /* Parse the packet with timeout */
-    silc_task_register(server->timeout_queue, sock->sock,
+    silc_schedule_task_add(server->schedule, sock->sock,
                       silc_server_packet_parse_real,
                       (void *)parser_context, 0, 100000,
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_NORMAL);
     break;
-  case SILC_SOCKET_TYPE_CLIENT:
-    /* Parse the packet with timeout (unless protocol is active) */
-    silc_task_register(server->timeout_queue, sock->sock,
-                      silc_server_packet_parse_real,
-                      (void *)parser_context, 0, 
-                      (sock->protocol ? 1 : 100000),
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
-    break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     /* Packets from servers are parsed as soon as possible */
-    silc_task_register(server->timeout_queue, sock->sock,
+    silc_schedule_task_add(server->schedule, sock->sock,
                       silc_server_packet_parse_real,
                       (void *)parser_context, 0, 1,
                       SILC_TASK_TIMEOUT,
@@ -1570,10 +1682,8 @@ void silc_server_packet_parse_type(SilcServer server,
     SILC_LOG_DEBUG(("Success packet"));
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
-    if (sock->protocol) {
-      sock->protocol->execute(server->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    }
+    if (sock->protocol)
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     break;
 
   case SILC_PACKET_FAILURE:
@@ -1592,7 +1702,7 @@ void silc_server_packet_parse_type(SilcServer server,
       f->sock = sock;
       
       /* We will wait 5 seconds to process this failure packet */
-      silc_task_register(server->timeout_queue, sock->sock,
+      silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_failure_callback, (void *)f, 5, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     }
@@ -1711,13 +1821,10 @@ void silc_server_packet_parse_type(SilcServer server,
       proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 100000);
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
                      "protocol active, packet dropped."));
-
-      /* XXX Trigger KE protocol?? Rekey actually, maybe. */
     }
     break;
 
@@ -1740,8 +1847,7 @@ void silc_server_packet_parse_type(SilcServer server,
        proto_ctx->packet = silc_packet_context_dup(packet);
 
        /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock, 0, 0);
+       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
       } else {
        SilcServerKEInternalContext *proto_ctx = 
          (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1757,9 +1863,8 @@ void silc_server_packet_parse_type(SilcServer server,
          break;
 
        /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock,
-                               0, 100000);
+       silc_protocol_execute(sock->protocol, server->schedule, 
+                             0, 100000);
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
@@ -1786,8 +1891,7 @@ void silc_server_packet_parse_type(SilcServer server,
        proto_ctx->packet = silc_packet_context_dup(packet);
 
        /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock, 0, 0);
+       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
       } else {
        SilcServerKEInternalContext *proto_ctx = 
          (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1803,9 +1907,8 @@ void silc_server_packet_parse_type(SilcServer server,
          break;
 
        /* Let the protocol handle the packet */
-       sock->protocol->execute(server->timeout_queue, 0, 
-                               sock->protocol, sock->sock,
-                               0, 100000);
+       silc_protocol_execute(sock->protocol, server->schedule, 
+                             0, 100000);
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
@@ -1845,8 +1948,7 @@ void silc_server_packet_parse_type(SilcServer server,
       proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 0);
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
                      "protocol active, packet dropped."));
@@ -1953,9 +2055,7 @@ void silc_server_packet_parse_type(SilcServer server,
       proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
-                             0, 100000);
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
                      "protocol active, packet dropped."));
@@ -1982,7 +2082,7 @@ void silc_server_create_connection(SilcServer server,
   sconn->remote_host = strdup(remote_host);
   sconn->remote_port = port;
 
-  silc_task_register(server->timeout_queue, 0, 
+  silc_schedule_task_add(server->schedule, 0, 
                     silc_server_connect_router,
                     (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
                     SILC_TASK_PRI_NORMAL);
@@ -1998,25 +2098,33 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final)
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
-  SILC_LOG_INFO(("Closing connection %s:%d [%s] (%d)", sock->hostname,
-                sock->port,  
-                (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
-                 sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
-                 sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                 "Router"), sock->sock));
-
   /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
+  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
 
   /* Unregister all tasks */
-  silc_task_unregister_by_fd(server->io_queue, sock->sock);
-  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
+  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
+  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
 
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
   server->sockets[sock->sock] = NULL;
 
-  silc_task_register(server->timeout_queue, 0, 
+  /* If sock->user_data is NULL then we'll check for active protocols
+     here since the silc_server_free_sock_user_data has not been called
+     for this connection. */
+  if (!sock->user_data) {
+    /* If any protocol is active cancel its execution. It will call
+       the final callback which will finalize the disconnection. */
+    if (sock->protocol) {
+      silc_protocol_cancel(sock->protocol, server->schedule);
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute_final(sock->protocol, server->schedule);
+      sock->protocol = NULL;
+      return;
+    }
+  }
+
+  silc_schedule_task_add(server->schedule, 0, 
                     silc_server_close_connection_final,
                     (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
                     SILC_TASK_PRI_NORMAL);
@@ -2092,9 +2200,9 @@ void silc_server_free_client_data(SilcServer server,
     if (sock->outbuf->data - sock->outbuf->head)
      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    silc_server_packet_send_real(server, sock, TRUE);
+    silc_packet_send(sock, TRUE);
 
-    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
     SILC_UNSET_OUTBUF_PENDING(sock);
     silc_buffer_clear(sock->outbuf);
   }
@@ -2103,8 +2211,7 @@ void silc_server_free_client_data(SilcServer server,
   if (notify && !server->standalone && server->router)
     silc_server_send_notify_signoff(server, server->router->connection,
                                    server->server_type == SILC_SERVER ?
-                                   FALSE : TRUE, client->id, 
-                                   SILC_ID_CLIENT_LEN, signoff);
+                                   FALSE : TRUE, client->id, signoff);
 
   /* Remove client from all channels */
   if (notify)
@@ -2118,7 +2225,7 @@ void silc_server_free_client_data(SilcServer server,
      into history (for WHOWAS command) for 5 minutes */
   i->server = server;
   i->client = client;
-  silc_task_register(server->timeout_queue, 0, 
+  silc_schedule_task_add(server->schedule, 0, 
                     silc_server_free_client_data_timeout,
                     (void *)i, 300, 0,
                     SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
@@ -2154,7 +2261,8 @@ void silc_server_free_sock_user_data(SilcServer server,
 
       /* 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, TRUE);
+      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. */
@@ -2183,9 +2291,118 @@ void silc_server_free_sock_user_data(SilcServer server,
     }
   }
 
+  /* If any protocol is active cancel its execution */
+  if (sock->protocol) {
+    silc_protocol_cancel(sock->protocol, server->schedule);
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    sock->protocol = NULL;
+  }
+
   sock->user_data = NULL;
 }
 
+/* Removes the client from channels and possibly removes the channels
+   as well.  After removing those channels that exist, their channel
+   keys are regnerated. This is called only by the function
+   silc_server_remove_clients_by_server. */
+
+static void silc_server_remove_clients_channels(SilcServer server, 
+                                               SilcSocketConnection sock,
+                                               SilcClientEntry client,
+                                               SilcHashTable channels)
+{
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer clidp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client || !client->id)
+    return;
+
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Remove the client from all channels. The client is removed from
+     the channels' user list. */
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    channel = chl->channel;
+
+    /* Remove channel from client's channel list */
+    silc_hash_table_del(client->channels, channel);
+
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_hash_table_count(channel->user_list) < 2) {
+
+      if (silc_hash_table_find(channels, channel, NULL, NULL))
+       silc_hash_table_del(channels, channel);
+
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
+      continue;
+    }
+
+    /* Remove client from channel's client list */
+    silc_hash_table_del(channel->user_list, chl->client);
+    silc_free(chl);
+    server->stat.my_chanclients--;
+
+    /* If there is no global users on the channel anymore mark the channel
+       as local channel. Do not check if the removed client is local client. */
+    if (server->server_type == SILC_SERVER && channel->global_users && 
+       chl->client->router && !silc_server_channel_has_global(channel))
+      channel->global_users = FALSE;
+
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_local(channel)) {
+
+      if (silc_hash_table_find(channels, channel, NULL, NULL))
+       silc_hash_table_del(channels, channel);
+
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+       SilcHashTableList htl2;
+
+       channel->id = NULL;
+
+       silc_hash_table_list(channel->user_list, &htl2);
+       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+         silc_hash_table_del(chl2->client->channels, channel);
+         silc_hash_table_del(channel->user_list, chl2->client);
+         silc_free(chl2);
+       }
+       continue;
+      }
+
+      /* Remove the channel entry */
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
+      continue;
+    }
+
+    /* Add the channel to the the channels list to regenerate the 
+       channel key */
+    if (!silc_hash_table_find(channels, channel, NULL, NULL))
+      silc_hash_table_add(channels, channel, channel);
+  }
+
+  silc_buffer_free(clidp);
+}
+
 /* 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'. 
@@ -2204,24 +2421,33 @@ int silc_server_remove_clients_by_server(SilcServer server,
   uint32 clients_c = 0;
   unsigned char **argv = NULL;
   uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  SilcHashTableList htl;
+  SilcChannelEntry channel;
+  SilcHashTable channels;
   int i;
 
   SILC_LOG_DEBUG(("Start"));
 
+  /* Allocate the hash table that holds the channels that require
+     channel key re-generation after we've removed this server's clients
+     from the channels. */
+  channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
+                                  NULL, NULL, TRUE);
+
   if (server_signoff) {
     idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
     argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
     argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
     argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
-    argv[argc] = idp->data;
+    argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+    memcpy(argv[argc], idp->data, idp->len);
     argv_lens[argc] = idp->len;
     argv_types[argc] = argc + 1;
     argc++;
     silc_buffer_free(idp);
   }
 
-  if (silc_idcache_find_by_id(server->local_list->clients, 
-                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+  if (silc_idcache_get_all(server->local_list->clients, &list)) {
 
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
@@ -2263,8 +2489,7 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, FALSE, 
-                                        NULL, FALSE);
+       silc_server_remove_clients_channels(server, NULL, client, channels);
        silc_idlist_del_client(server->local_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2274,8 +2499,7 @@ int silc_server_remove_clients_by_server(SilcServer server,
     silc_idcache_list_free(list);
   }
   
-  if (silc_idcache_find_by_id(server->global_list->clients, 
-                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+  if (silc_idcache_get_all(server->global_list->clients, &list)) {
 
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
@@ -2317,8 +2541,7 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, FALSE,
-                                        NULL, FALSE);
+       silc_server_remove_clients_channels(server, NULL, client, channels);
        silc_idlist_del_client(server->global_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2333,13 +2556,14 @@ int silc_server_remove_clients_by_server(SilcServer server,
     SilcBuffer args;
 
     /* Send SERVER_SIGNOFF notify to our primary router */
-    if (!server->standalone && server->router) {
+    if (!server->standalone && server->router &&
+       server->router != entry) {
       args = silc_argument_payload_encode(1, argv, argv_lens,
                                          argv_types);
       silc_server_send_notify_args(server, 
                                   server->router->connection,
-                                  server->server_type == 
-                                  SILC_SERVER ? FALSE : TRUE, 
+                                  server->server_type == SILC_SERVER ? 
+                                  FALSE : TRUE, 
                                   SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
                                   argc, args);
       silc_buffer_free(args);
@@ -2356,11 +2580,25 @@ int silc_server_remove_clients_by_server(SilcServer server,
 
     silc_free(clients);
     silc_buffer_free(args);
+    for (i = 0; i < argc; i++)
+      silc_free(argv[i]);
     silc_free(argv);
     silc_free(argv_lens);
     silc_free(argv_types);
   }
 
+  /* We must now re-generate the channel key for all channels that had
+     this server's client(s) on the channel. As they left the channel we
+     must re-generate the channel key. */
+  silc_hash_table_list(channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
+    silc_server_create_channel_key(server, channel, 0);
+    silc_server_send_channel_key(server, NULL, channel, 
+                                server->server_type == SILC_ROUTER ? 
+                                FALSE : !server->standalone);
+  }
+  silc_hash_table_free(channels);
+
   return TRUE;
 }
 
@@ -2370,9 +2608,10 @@ int silc_server_remove_clients_by_server(SilcServer server,
 int silc_server_channel_has_global(SilcChannelEntry channel)
 {
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
 
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     if (chl->client->router)
       return TRUE;
   }
@@ -2386,9 +2625,10 @@ int silc_server_channel_has_global(SilcChannelEntry channel)
 int silc_server_channel_has_local(SilcChannelEntry channel)
 {
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
 
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     if (!chl->client->router)
       return TRUE;
   }
@@ -2409,6 +2649,7 @@ void silc_server_remove_from_channels(SilcServer server,
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
@@ -2420,50 +2661,33 @@ void silc_server_remove_from_channels(SilcServer server,
 
   /* Remove the client from all channels. The client is removed from
      the channels' user list. */
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     channel = chl->channel;
 
     /* Remove channel from client's channel list */
-    silc_list_del(client->channels, chl);
+    silc_hash_table_del(client->channels, channel);
 
     /* Remove channel if there is no users anymore */
     if (server->server_type == SILC_ROUTER &&
-       silc_list_count(channel->user_list) < 2) {
-      server->stat.my_channels--;
-
+       silc_hash_table_count(channel->user_list) < 2) {
       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;
-      }
-
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
       continue;
     }
 
     /* Remove client from channel's client list */
-    silc_list_del(channel->user_list, chl);
+    silc_hash_table_del(channel->user_list, chl->client);
     silc_free(chl);
     server->stat.my_chanclients--;
 
     /* If there is no global users on the channel anymore mark the channel
-       as local channel. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_global(channel))
+       as local channel. Do not check if the removed client is local client. */
+    if (server->server_type == SILC_SERVER && channel->global_users && 
+       chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
     /* If there is not at least one local user on the channel then we don't
@@ -2479,28 +2703,29 @@ void silc_server_remove_from_channels(SilcServer server,
                                           signoff_message, signoff_message ?
                                           strlen(signoff_message) : 0);
 
-      server->stat.my_channels--;
-
       if (channel->rekey)
-       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 
       if (channel->founder_key) {
        /* The founder auth data exists, do not remove the channel entry */
        SilcChannelClientEntry chl2;
+       SilcHashTableList htl2;
 
        channel->id = NULL;
 
-       silc_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_hash_table_list(channel->user_list, &htl2);
+       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+         silc_hash_table_del(chl2->client->channels, channel);
+         silc_hash_table_del(channel->user_list, chl2->client);
          silc_free(chl2);
        }
        continue;
       }
 
+      /* Remove the channel entry */
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
       continue;
     }
 
@@ -2541,93 +2766,91 @@ int silc_server_remove_from_one_channel(SilcServer server,
                                        SilcClientEntry client,
                                        int notify)
 {
-  SilcChannelEntry ch;
   SilcChannelClientEntry chl;
   SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
 
-  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  /* Get the entry to the channel, if this client is not on the channel
+     then return Ok. */
+  if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl))
+    return TRUE;
 
   /* Remove the client from the channel. The client is removed from
      the channel's user list. */
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
-    if (chl->channel != channel)
-      continue;
-
-    ch = chl->channel;
-
-    /* Remove channel from client's channel list */
-    silc_list_del(client->channels, chl);
-
-    /* Remove channel if there is no users anymore */
-    if (server->server_type == SILC_ROUTER &&
-       silc_list_count(channel->user_list) < 2) {
-      if (channel->rekey)
-       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      silc_buffer_free(clidp);
-      server->stat.my_channels--;
-      return FALSE;
-    }
-
-    /* Remove client from channel's client list */
-    silc_list_del(channel->user_list, chl);
-    silc_free(chl);
-    server->stat.my_chanclients--;
-
-    /* If there is no global users on the channel anymore mark the channel
-       as local channel. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_global(channel))
-      channel->global_users = FALSE;
-
-    /* If there is not at least one local user on the channel then we don't
-       need the channel entry anymore, we can remove it safely. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_local(channel)) {
-      /* Notify about leaving client if this channel has global users. */
-      if (notify && channel->global_users)
-       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                          SILC_NOTIFY_TYPE_LEAVE, 1,
-                                          clidp->data, clidp->len);
 
-      server->stat.my_channels--;
-      silc_buffer_free(clidp);
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-      if (channel->rekey)
-       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
+  /* Remove channel from client's channel list */
+  silc_hash_table_del(client->channels, chl->channel);
 
-      if (channel->founder_key) {
-       /* The founder auth data exists, do not remove the channel entry */
-       SilcChannelClientEntry chl2;
+  /* Remove channel if there is no users anymore */
+  if (server->server_type == SILC_ROUTER &&
+      silc_hash_table_count(channel->user_list) < 2) {
+    if (channel->rekey)
+      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+    if (!silc_idlist_del_channel(server->local_list, channel))
+      silc_idlist_del_channel(server->global_list, channel);
+    silc_buffer_free(clidp);
+    server->stat.my_channels--;
+    return FALSE;
+  }
 
-       channel->id = NULL;
+  /* Remove client from channel's client list */
+  silc_hash_table_del(channel->user_list, chl->client);
+  silc_free(chl);
+  server->stat.my_chanclients--;
+  
+  /* If there is no global users on the channel anymore mark the channel
+     as local channel. Do not check if the client is local client. */
+  if (server->server_type == SILC_SERVER && channel->global_users &&
+      chl->client->router && !silc_server_channel_has_global(channel))
+    channel->global_users = FALSE;
+
+  /* If there is not at least one local user on the channel then we don't
+     need the channel entry anymore, we can remove it safely. */
+  if (server->server_type == SILC_SERVER &&
+      !silc_server_channel_has_local(channel)) {
+    /* Notify about leaving client if this channel has global users. */
+    if (notify && channel->global_users)
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                        SILC_NOTIFY_TYPE_LEAVE, 1,
+                                        clidp->data, clidp->len);
+    
+    silc_buffer_free(clidp);
+    
+    if (channel->rekey)
+      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
 
-       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 (channel->founder_key) {
+      /* The founder auth data exists, do not remove the channel entry */
+      SilcChannelClientEntry chl2;
+      SilcHashTableList htl2;
+      
+      channel->id = NULL;
+      
+      silc_hash_table_list(channel->user_list, &htl2);
+      while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+       silc_hash_table_del(chl2->client->channels, channel);
+       silc_hash_table_del(channel->user_list, chl2->client);
+       silc_free(chl2);
       }
-
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
       return FALSE;
     }
 
-    /* Send notify to channel about client leaving the channel */
-    if (notify)
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                        SILC_NOTIFY_TYPE_LEAVE, 1,
-                                        clidp->data, clidp->len);
-    break;
+    /* Remove the channel entry */
+    if (!silc_idlist_del_channel(server->local_list, channel))
+      silc_idlist_del_channel(server->global_list, channel);
+    server->stat.my_channels--;
+    return FALSE;
   }
 
+  /* Send notify to channel about client leaving the channel */
+  if (notify)
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_LEAVE, 1,
+                                      clidp->data, clidp->len);
+
   silc_buffer_free(clidp);
   return TRUE;
 }
@@ -2640,15 +2863,11 @@ int silc_server_remove_from_one_channel(SilcServer server,
 int silc_server_client_on_channel(SilcClientEntry client,
                                  SilcChannelEntry channel)
 {
-  SilcChannelClientEntry chl;
-
   if (!client || !channel)
     return FALSE;
 
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END)
-    if (chl->channel == channel)
-      return TRUE;
+  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
+    return TRUE;
 
   return FALSE;
 }
@@ -2662,14 +2881,23 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = server->sockets[fd];
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (!sock)
     return;
 
+  /* If we have protocol active we must assure that we call the protocol's
+     final callback so that all the memory is freed. */
+  if (sock->protocol) {
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    return;
+  }
+
   if (sock->user_data)
     silc_server_free_sock_user_data(server, sock);
 
-  silc_server_disconnect_remote(server, sock, 
-                               "Server closed connection: "
+  silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                "Connection timeout");
 }
 
@@ -2716,6 +2944,8 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
                                  NULL, key, newhmac);
   if (!entry) {
     silc_free(channel_name);
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
     return NULL;
   }
 
@@ -2730,7 +2960,8 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
      to our primary route. */
   if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                channel_name, entry->id, 
+                                silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
   server->stat.my_channels++;
@@ -2788,7 +3019,8 @@ silc_server_create_new_channel_with_id(SilcServer server,
      to our primary route. */
   if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                channel_name, entry->id, 
+                                silc_id_get_len(entry->id, SILC_ID_CHANNEL),
                                 entry->mode);
 
   server->stat.my_channels++;
@@ -2806,7 +3038,7 @@ SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
   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_schedule_task_add(server->schedule, 0, 
                     silc_server_channel_key_rekey,
                     (void *)rekey, 3600, 0,
                     SILC_TASK_TIMEOUT,
@@ -2875,9 +3107,9 @@ void silc_server_create_channel_key(SilcServer server,
     channel->rekey->channel = channel;
     channel->rekey->key_len = key_len;
 
-    silc_task_unregister_by_callback(server->timeout_queue,
+    silc_schedule_task_del_by_callback(server->schedule,
                                     silc_server_channel_key_rekey);
-    silc_task_register(server->timeout_queue, 0, 
+    silc_schedule_task_add(server->schedule, 0, 
                       silc_server_channel_key_rekey,
                       (void *)channel->rekey, 3600, 0,
                       SILC_TASK_TIMEOUT,
@@ -2980,9 +3212,9 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
 
-    silc_task_unregister_by_callback(server->timeout_queue,
+    silc_schedule_task_del_by_callback(server->schedule,
                                     silc_server_channel_key_rekey);
-    silc_task_register(server->timeout_queue, 0, 
+    silc_schedule_task_add(server->schedule, 0, 
                       silc_server_channel_key_rekey,
                       (void *)channel->rekey, 3600, 0,
                       SILC_TASK_TIMEOUT,
@@ -3027,8 +3259,7 @@ static void silc_server_announce_get_servers(SilcServer server,
   SilcBuffer idp;
 
   /* Go through all clients in the list */
-  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
-                             SILC_ID_SERVER, &list)) {
+  if (silc_idcache_get_all(id_list->servers, &list)) {
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        entry = (SilcServerEntry)id_cache->context;
@@ -3094,8 +3325,7 @@ static void silc_server_announce_get_clients(SilcServer server,
   SilcBuffer idp;
 
   /* Go through all clients in the list */
-  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
-                             SILC_ID_CLIENT, &list)) {
+  if (silc_idcache_get_all(id_list->clients, &list)) {
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
@@ -3152,34 +3382,44 @@ void silc_server_announce_clients(SilcServer server)
 }
 
 static SilcBuffer 
-silc_server_announce_encode_join(uint32 argc, ...)
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
 {
   va_list ap;
+  SilcBuffer p;
 
   va_start(ap, argc);
-  return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
+  p = silc_notify_payload_encode(notify, argc, ap);
+  va_end(ap);
+  return p;
 }
 
 /* Returns assembled packets for channel users of the `channel'. */
 
 void silc_server_announce_get_channel_users(SilcServer server,
                                            SilcChannelEntry channel,
-                                           SilcBuffer *channel_users)
+                                           SilcBuffer *channel_users,
+                                           SilcBuffer *channel_users_modes)
 {
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   SilcBuffer chidp, clidp;
   SilcBuffer tmp;
   int len;
+  unsigned char mode[4];
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Now find all users on the channel */
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
-    tmp = silc_server_announce_encode_join(2, clidp->data, clidp->len,
-                                          chidp->data, chidp->len);
+
+    /* 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, 
@@ -3191,8 +3431,28 @@ void silc_server_announce_get_channel_users(SilcServer server,
     
     silc_buffer_put(*channel_users, tmp->data, tmp->len);
     silc_buffer_pull(*channel_users, len);
-    silc_buffer_free(clidp);
     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);
 }
@@ -3204,28 +3464,33 @@ void silc_server_announce_get_channel_users(SilcServer server,
 void silc_server_announce_get_channels(SilcServer server,
                                       SilcIDList id_list,
                                       SilcBuffer *channels,
-                                      SilcBuffer *channel_users)
+                                      SilcBuffer *channel_users,
+                                      SilcBuffer **channel_users_modes,
+                                      uint32 *channel_users_modes_c,
+                                      SilcChannelID ***channel_ids)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
   SilcChannelEntry channel;
   unsigned char *cid;
+  uint32 id_len;
   uint16 name_len;
   int len;
+  int i = *channel_users_modes_c;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Go through all channels in the list */
-  if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY, 
-                             SILC_ID_CHANNEL, &list)) {
+  if (silc_idcache_get_all(id_list->channels, &list)) {
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        channel = (SilcChannelEntry)id_cache->context;
        
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
-       len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+       len = 4 + name_len + id_len + 4;
        *channels = 
          silc_buffer_realloc(*channels, 
                              (*channels ? (*channels)->truelen + len : len));
@@ -3235,20 +3500,30 @@ void silc_server_announce_get_channels(SilcServer server,
                           SILC_STR_UI_SHORT(name_len),
                           SILC_STR_UI_XNSTRING(channel->channel_name, 
                                                name_len),
-                          SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                          SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                          SILC_STR_UI_SHORT(id_len),
+                          SILC_STR_UI_XNSTRING(cid, id_len),
                           SILC_STR_UI_INT(channel->mode),
                           SILC_STR_END);
        silc_buffer_pull(*channels, len);
 
+       *channel_users_modes = silc_realloc(*channel_users_modes,
+                                           sizeof(**channel_users_modes) * 
+                                           (i + 1));
+       (*channel_users_modes)[i] = NULL;
+       *channel_ids = silc_realloc(*channel_ids, 
+                                   sizeof(**channel_ids) * (i + 1));
+       (*channel_ids)[i] = NULL;
        silc_server_announce_get_channel_users(server, channel,
-                                              channel_users);
-
-       silc_free(cid);
+                                              channel_users,
+                                              channel_users_modes[i]);
+       (*channel_ids)[i] = channel->id;
+       i++;
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
       }
+
+      *channel_users_modes_c += i;
     }
 
     silc_idcache_list_free(list);
@@ -3262,16 +3537,25 @@ void silc_server_announce_get_channels(SilcServer server,
 void silc_server_announce_channels(SilcServer server)
 {
   SilcBuffer channels = NULL, channel_users = NULL;
+  SilcBuffer *channel_users_modes = NULL;
+  uint32 channel_users_modes_c = 0;
+  SilcChannelID **channel_ids = NULL;
 
   SILC_LOG_DEBUG(("Announcing channels and channel users"));
 
   /* Get channels and channel users in local list */
   silc_server_announce_get_channels(server, server->local_list,
-                                   &channels, &channel_users);
+                                   &channels, &channel_users,
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_ids);
 
   /* Get channels and channel users in global list */
   silc_server_announce_get_channels(server, server->global_list,
-                                   &channels, &channel_users);
+                                   &channels, &channel_users,
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_ids);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
@@ -3299,6 +3583,27 @@ void silc_server_announce_channels(SilcServer server)
 
     silc_buffer_free(channel_users);
   }
+
+  if (channel_users_modes) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      silc_buffer_push(channel_users_modes[i], 
+                      channel_users_modes[i]->data - 
+                      channel_users_modes[i]->head);
+      SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data, 
+                      channel_users_modes[i]->len);
+      silc_server_packet_send_dest(server, server->router->connection,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_users_modes[i]->data, 
+                                  channel_users_modes[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_users_modes[i]);
+    }
+    silc_free(channel_users_modes);
+    silc_free(channel_ids);
+  }
 }
 
 /* Failure timeout callback. If this is called then we will immediately
@@ -3314,8 +3619,7 @@ SILC_TASK_CALLBACK(silc_server_failure_callback)
 
   if (f->sock->protocol) {
     f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-    f->sock->protocol->execute(f->server->timeout_queue, 0,
-                              f->sock->protocol, f->sock->sock, 0, 0);
+    silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
   }
 
   silc_free(f);
@@ -3330,19 +3634,24 @@ void silc_server_get_users_on_channel(SilcServer server,
                                      uint32 *user_count)
 {
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   SilcBuffer idp;
-  uint32 list_count = 0;
+  uint32 list_count = 0, len = 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_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+    len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
+
+  client_id_list = silc_buffer_alloc(len);
+  client_mode_list = 
+    silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
   silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
   silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     /* Client ID */
     idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
     silc_buffer_put(client_id_list, idp->data, idp->len);
@@ -3398,7 +3707,7 @@ void silc_server_save_users_on_channel(SilcServer server,
     SILC_GET32_MSB(mode, mode_list->data);
     silc_buffer_pull(mode_list, 4);
 
-    if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+    if (noadd && SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
       silc_free(client_id);
       continue;
     }
@@ -3420,8 +3729,7 @@ void silc_server_save_users_on_channel(SilcServer server,
       /* We don't have that client anywhere, add it. The client is added
         to global list since server didn't have it in the lists so it must be 
         global. */
-      client = silc_idlist_add_client(server->global_list, NULL, 0, NULL, 
-                                     NULL, 
+      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
                                      silc_id_dup(client_id, SILC_ID_CLIENT), 
                                      sock->user_data, NULL);
       if (!client) {
@@ -3440,8 +3748,8 @@ void silc_server_save_users_on_channel(SilcServer server,
       chl->client = client;
       chl->mode = mode;
       chl->channel = channel;
-      silc_list_add(channel->user_list, chl);
-      silc_list_add(client->channels, chl);
+      silc_hash_table_add(channel->user_list, chl->client, chl);
+      silc_hash_table_add(client->channels, chl->channel, chl);
     }
   }
 }
@@ -3479,15 +3787,15 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
   if (client) {
     silc_free(id);
 
-    if (client && client->data.registered == FALSE)
+    if (client->data.registered == FALSE)
       return NULL;
 
     /* If we are router and the client has router then the client is in
        our cell but not directly connected to us. */
     if (server->server_type == SILC_ROUTER && client->router) {
-      /* We are of course in this case the client's router thus the real
-        "router" of the client is the server who owns the client. Thus
-        we will send the packet to that server. */
+      /* We are of course in this case the client's router thus the route
+        to the client is the server who owns the client. So, we will send
+        the packet to that server. */
       if (idata)
        *idata = (SilcIDListData)client->router;
       return client->router->connection;
@@ -3538,21 +3846,24 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   SilcBuffer buffer = NULL;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
+  SilcHashTableList htl;
   unsigned char *cid;
+  uint32 id_len;
   uint16 name_len;
   int len;
 
-  silc_list_start(client->channels);
-  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     channel = chl->channel;
 
     if (channel->mode & SILC_CHANNEL_MODE_SECRET)
       continue;
 
     cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+    id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
     name_len = strlen(channel->channel_name);
     
-    len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+    len = 4 + name_len + id_len + 4;
     buffer = silc_buffer_realloc(buffer, 
                                 (buffer ? (buffer)->truelen + len : len));
     silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
@@ -3560,8 +3871,8 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
                       SILC_STR_UI_SHORT(name_len),
                       SILC_STR_UI_XNSTRING(channel->channel_name, 
                                            name_len),
-                      SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                      SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                      SILC_STR_UI_SHORT(id_len),
+                      SILC_STR_UI_XNSTRING(cid, id_len),
                       SILC_STR_UI_INT(chl->mode), /* Client's mode */
                       SILC_STR_END);
     silc_buffer_pull(buffer, len);
@@ -3606,6 +3917,7 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
                            buffer->data, buffer->len, FALSE);
     silc_buffer_free(idp);
     silc_buffer_free(buffer);
+    return NULL;
   }
 
   return client;
@@ -3639,11 +3951,10 @@ SILC_TASK_CALLBACK(silc_server_rekey_callback)
   sock->protocol = protocol;
       
   /* Run the protocol */
-  protocol->execute(server->timeout_queue, 0, protocol, 
-                   sock->sock, 0, 0);
+  silc_protocol_execute(protocol, server->schedule, 0, 0);
 
   /* Re-register re-key timeout */
-  silc_task_register(server->timeout_queue, sock->sock, 
+  silc_schedule_task_add(server->schedule, sock->sock, 
                     silc_server_rekey_callback,
                     context, idata->rekey->timeout, 0,
                     SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
@@ -3665,7 +3976,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    silc_protocol_cancel(server->timeout_queue, protocol);
+    silc_protocol_cancel(protocol, server->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
     if (ctx->packet)
@@ -3676,12 +3987,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     return;
   }
 
-  /* Take the keys into use */
-  if (ctx->pfs == TRUE)
-    silc_server_protocol_rekey_generate_pfs(server, ctx);
-  else
-    silc_server_protocol_rekey_generate(server, ctx);
-
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;