Code auditing weekend results and fixes committing.
[silc.git] / apps / silcd / server.c
index 691bd524a82fc30d46fed644612a8496565176c6..26b214fd3aef7adf1900caa2cd8f0204d25d7be6 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
  * servicing the SILC connections. This is also a SILC router as a router 
  * is also normal server.
  */
-/*
- * $Id$
- * $Log$
- * Revision 1.8  2000/07/12 05:59:41  priikone
- *     Major rewrite of ID Cache system. Support added for the new
- *     ID cache system. Major rewrite of ID List stuff on server.  All
- *     SilcXXXList's are now called SilcXXXEntry's and they are pointers
- *     by default. A lot rewritten ID list functions.
- *
- * Revision 1.7  2000/07/10 05:43:00  priikone
- *     Removed command packet processing from server.c and added it to
- *     command.c.
- *     Implemented INFO command. Added support for testing that
- *     connections are registered before executing commands.
- *
- * Revision 1.6  2000/07/07 06:55:59  priikone
- *     Added SILC style public key support and made server to use
- *     it at all time.
- *
- * Revision 1.5  2000/07/06 13:18:07  priikone
- *     Check for NULL in client_on_channel.
- *
- * Revision 1.4  2000/07/05 06:14:01  priikone
- *     Global costemic changes.
- *
- * Revision 1.3  2000/07/04 08:13:53  priikone
- *     Changed message route discovery to use silc_server_get_route.
- *     Added silc_server_client_on_channel function.
- *
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Imported from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 
 /* Static prototypes */
+SILC_TASK_CALLBACK(silc_server_connect_router);
 SILC_TASK_CALLBACK(silc_server_connect_to_router);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
@@ -68,26 +36,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection);
 SILC_TASK_CALLBACK(silc_server_accept_new_connection_second);
 SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
 SILC_TASK_CALLBACK(silc_server_packet_process);
-SILC_TASK_CALLBACK(silc_server_packet_parse);
+SILC_TASK_CALLBACK(silc_server_packet_parse_real);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
 
-/* XXX */
-void silc_server_packet_parse_type(SilcServer server, 
-                                  SilcSocketConnection sock,
-                                  SilcPacketContext *packet);
-
-static int silc_server_packet_check_mac(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer buffer);
-static int silc_server_packet_decrypt_rest(SilcServer server, 
-                                          SilcSocketConnection sock,
-                                          SilcBuffer buffer);
-static int silc_server_packet_decrypt_rest_special(SilcServer server, 
-                                                  SilcSocketConnection sock,
-                                                  SilcBuffer buffer);
-
-extern char server_version[];
-
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
    the server. The new allocated server object is returned to the new_server
@@ -101,9 +52,13 @@ int silc_server_alloc(SilcServer *new_server)
 
   server = silc_calloc(1, sizeof(*server));
   server->server_type = SILC_SERVER;
-  server->standalone = FALSE;
+  server->standalone = TRUE;
   server->local_list = silc_calloc(1, sizeof(*server->local_list));
   server->global_list = silc_calloc(1, sizeof(*server->global_list));
+  server->pending_commands = silc_dlist_init();
+#ifdef SILC_SIM
+  server->sim = silc_dlist_init();
+#endif
 
   *new_server = server;
 
@@ -116,6 +71,10 @@ int silc_server_alloc(SilcServer *new_server)
 void silc_server_free(SilcServer server)
 {
   if (server) {
+#ifdef SILC_SIM
+    SilcSimContext *sim;
+#endif
+
     if (server->local_list)
       silc_free(server->local_list);
     if (server->global_list)
@@ -123,6 +82,20 @@ void silc_server_free(SilcServer server)
     if (server->rng)
       silc_rng_free(server->rng);
 
+#ifdef SILC_SIM
+    while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
+      silc_dlist_del(server->sim, sim);
+      silc_sim_free(sim);
+    }
+    silc_dlist_uninit(server->sim);
+#endif
+
+    if (server->params)
+      silc_free(server->params);
+
+    if (server->pending_commands)
+      silc_dlist_uninit(server->pending_commands);
+
     silc_math_primegen_uninit(); /* XXX */
     silc_free(server);
   }
@@ -145,6 +118,16 @@ int silc_server_init(SilcServer server)
   assert(server);
   assert(server->config);
 
+  /* XXX After server is made as Silc Server Library this can be given
+     as argument, for now this is hard coded */
+  server->params = silc_calloc(1, sizeof(*server->params));
+  server->params->retry_count = SILC_SERVER_RETRY_COUNT;
+  server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN;
+  server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
+  server->params->retry_keep_trying = FALSE;
+  server->params->protocol_timeout = 60;
+  server->params->require_reverse_mapping = FALSE;
+
   /* Set log files where log message should be saved. */
   server->config->server = server;
   silc_config_server_setlogfiles(server->config);
@@ -241,11 +224,12 @@ int silc_server_init(SilcServer server)
   server->local_list->servers = silc_idcache_alloc(0);
   server->local_list->channels = silc_idcache_alloc(0);
 
-  if (server->server_type == SILC_ROUTER) {
-    server->global_list->clients = silc_idcache_alloc(0);
-    server->global_list->servers = silc_idcache_alloc(0);
-    server->global_list->channels = silc_idcache_alloc(0);
-  }
+  /* These are allocated for normal server as well as these hold some 
+     global information that the server has fetched from its router. For 
+     router these are used as they are supposed to be used on router. */
+  server->global_list->clients = silc_idcache_alloc(0);
+  server->global_list->servers = silc_idcache_alloc(0);
+  server->global_list->channels = silc_idcache_alloc(0);
 
   /* Allocate the entire socket list that is used in server. Eventually 
      all connections will have entry in this table (it is a table of 
@@ -268,6 +252,8 @@ int silc_server_init(SilcServer server)
     }
     
     server->id = id;
+    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
+    server->id_string_len = silc_id_get_len(SILC_ID_SERVER);
     server->id_type = SILC_ID_SERVER;
     server->server_name = server->config->server_info->server_name;
 
@@ -278,9 +264,7 @@ int silc_server_init(SilcServer server)
     id_entry = 
       silc_idlist_add_server(server->local_list,
                             server->config->server_info->server_name,
-                            server->server_type, server->id, NULL,
-                            server->send_key, server->receive_key,
-                            NULL, NULL, NULL, NULL);
+                            server->server_type, server->id, NULL, NULL);
     if (!id_entry) {
       SILC_LOG_ERROR(("Could not add ourselves to cache"));
       goto err0;
@@ -318,22 +302,32 @@ int silc_server_init(SilcServer server)
     goto err1;
   }
 
+  /* Register protocols */
+  silc_server_protocols_register();
+
   /* Initialize the scheduler */
-  silc_schedule_init(server->io_queue, server->timeout_queue, 
-                    server->generic_queue, 
+  silc_schedule_init(&server->io_queue, &server->timeout_queue, 
+                    &server->generic_queue, 
                     SILC_SERVER_MAX_CONNECTIONS);
   
   /* Add the first task to the queue. This is task that is executed by
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  if (silc_task_register(server->timeout_queue, sock[0], 
-                        silc_server_connect_to_router,
-                        (void *)server, 0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL) == NULL) {
-    goto err2;
-  }
+  silc_task_register(server->timeout_queue, sock[0], 
+                    silc_server_connect_to_router,
+                    (void *)server, 0, 1,
+                    SILC_TASK_TIMEOUT,
+                    SILC_TASK_PRI_NORMAL);
+
+  /* Add listener task to the queue. This task receives new connections to the 
+     server. This task remains on the queue until the end of the program. */
+  silc_task_register(server->io_queue, sock[0],
+                    silc_server_accept_new_connection,
+                    (void *)server, 0, 0, 
+                    SILC_TASK_FD,
+                    SILC_TASK_PRI_NORMAL);
+  server->listenning = TRUE;
 
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
@@ -345,7 +339,6 @@ int silc_server_init(SilcServer server)
   /* We are done here, return succesfully */
   return TRUE;
 
- err2:
   silc_task_queue_free(server->timeout_queue);
  err1:
   silc_task_queue_free(server->io_queue);
@@ -370,10 +363,14 @@ void silc_server_stop(SilcServer server)
   silc_schedule_stop();
   silc_schedule_uninit();
 
+  silc_server_protocols_unregister();
+
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
-/* The heart of the server. This runs the scheduler thus runs the server. */
+/* The heart of the server. This runs the scheduler thus runs the server. 
+   When this returns the server has been stopped and the program will
+   be terminated. */
 
 void silc_server_run(SilcServer server)
 {
@@ -384,6 +381,120 @@ void silc_server_run(SilcServer server)
   silc_schedule();
 }
 
+/* Timeout callback that will be called to retry connecting to remote
+   router. This is used by both normal and router server. This will wait
+   before retrying the connecting. The timeout is generated by exponential
+   backoff algorithm. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+{
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+
+  SILC_LOG_INFO(("Retrying connecting to a router"));
+
+  /* Calculate next timeout */
+  if (sconn->retry_count >= 1) {
+    sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+    if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
+      sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
+  } else {
+    sconn->retry_timeout = server->params->retry_interval_min;
+  }
+  sconn->retry_count++;
+  sconn->retry_timeout = sconn->retry_timeout +
+    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
+
+  /* If we've reached max retry count, give up. */
+  if (sconn->retry_count > server->params->retry_count && 
+      server->params->retry_keep_trying == FALSE) {
+    SILC_LOG_ERROR(("Could not connect to router, giving up"));
+    return;
+  }
+
+  /* Wait one before retrying */
+  silc_task_register(server->timeout_queue, fd, silc_server_connect_router,
+                    context, sconn->retry_timeout, 
+                    server->params->retry_interval_min_usec,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* Generic routine to use connect to a router. */
+
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{    
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+  SilcSocketConnection newsocket;
+  SilcProtocol protocol;
+  SilcServerKEInternalContext *proto_ctx;
+  int sock;
+
+  /* Connect to remote host */
+  sock = silc_net_create_connection(sconn->remote_port, 
+                                   sconn->remote_host);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Could not connect to router"));
+    silc_task_register(server->timeout_queue, fd, 
+                      silc_server_connect_to_router_retry,
+                      context, 0, 1, SILC_TASK_TIMEOUT, 
+                      SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* Create socket connection for the connection. Even though we
+     know that we are connecting to a router we will mark the socket
+     to be unknown connection until we have executed authentication
+     protocol. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
+  newsocket->hostname = strdup(sconn->remote_host);
+  newsocket->port = sconn->remote_port;
+  sconn->sock = newsocket;
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->context = (void *)sconn;
+  proto_ctx->sock = newsocket;
+  proto_ctx->rng = server->rng;
+  proto_ctx->responder = FALSE;
+      
+  /* Perform key exchange protocol. silc_server_connect_to_router_second
+     will be called after the protocol is finished. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
+                     &protocol, proto_ctx,
+                     silc_server_connect_to_router_second);
+  newsocket->protocol = protocol;
+      
+  /* Register a timeout task that will be executed if the protocol
+     is not executed within set limit. */
+  proto_ctx->timeout_task = 
+    silc_task_register(server->timeout_queue, sock, 
+                      silc_server_timeout_remote,
+                      server, server->params->protocol_timeout,
+                      server->params->protocol_timeout_usec,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_LOW);
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as 
+     well. However, this doesn't set the scheduler for outgoing traffic,
+     it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)server;
+  SILC_REGISTER_CONNECTION_FOR_IO(sock);
+  
+  /* Run the protocol */
+  protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+}
+  
 /* This function connects to our primary router or if we are a router this
    establishes all our primary routes. This is called at the start of the
    server to do authentication and key exchange with our router - called
@@ -392,148 +503,59 @@ void silc_server_run(SilcServer server)
 SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
-  int sock;
+  SilcServerConnection sconn;
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
-  /* if we are normal SILC server we need to connect to our cell's
+  /* If we are normal SILC server we need to connect to our cell's
      router. */
   if (server->server_type == SILC_SERVER) {
-    SilcProtocol protocol;
-    SilcServerKEInternalContext *proto_ctx;
+    SILC_LOG_DEBUG(("We are normal server"));
 
     /* Create connection to the router, if configured. */
     if (server->config->routers) {
-      sock = silc_net_create_connection(server->config->routers->port, 
-                                       server->config->routers->host);
-      if (sock < 0) {
-       SILC_LOG_ERROR(("Could not connect to router"));
-       silc_schedule_stop();
-       return;
-      }
 
-      /* Set socket options */
-      silc_net_set_socket_nonblock(sock);
-      silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-      /* Create socket connection for the connection. Even though we
-        know that we are connecting to a router we will mark the socket
-        to be unknown connection until we have executed authentication
-        protocol. */
-      silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-      server->sockets[sock] = newsocket;
-      newsocket->hostname = server->config->routers->host;
-      newsocket->port = server->config->routers->port;
-
-      /* Allocate internal protocol context. This is sent as context
-        to the protocol. */
-      proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-      proto_ctx->server = context;
-      proto_ctx->sock = newsocket;
-      proto_ctx->rng = server->rng;
-      proto_ctx->responder = FALSE;
-      
-      /* Perform key exchange protocol. silc_server_connect_to_router_second
-        will be called after the protocol is finished. */
-      silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                         &protocol, proto_ctx,
-                         silc_server_connect_to_router_second);
-      newsocket->protocol = protocol;
-      
-      /* Register a timeout task that will be executed if the protocol
-        is not executed within 60 seconds. For now, this is a hard coded 
-        limit. After 60 secs the connection will be closed if the key 
-        exchange protocol has not been executed. */
-      proto_ctx->timeout_task = 
-       silc_task_register(server->timeout_queue, sock, 
-                          silc_server_timeout_remote,
-                          context, 60, 0,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_LOW);
-
-      /* Register the connection for network input and output. This sets
-        that scheduler will listen for incoming packets for this connection 
-        and sets that outgoing packets may be sent to this connection as 
-        well. However, this doesn't set the scheduler for outgoing traffic,
-        it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-        later when outgoing data is available. */
-      SILC_REGISTER_CONNECTION_FOR_IO(sock);
-      
-      /* Run the protocol */
-      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+      /* Allocate connection object for hold connection specific stuff. */
+      sconn = silc_calloc(1, sizeof(*sconn));
+      sconn->server = server;
+      sconn->remote_host = server->config->routers->host;
+      sconn->remote_port = server->config->routers->port;
+
+      silc_task_register(server->timeout_queue, fd, 
+                        silc_server_connect_router,
+                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
       return;
     }
   }
-  
-  /* if we are a SILC router we need to establish all of our primary
+
+  /* If we are a SILC router we need to establish all of our primary
      routes. */
   if (server->server_type == SILC_ROUTER) {
     SilcConfigServerSectionServerConnection *ptr;
 
+    SILC_LOG_DEBUG(("We are router"));
+
     /* Create the connections to all our routes */
     ptr = server->config->routers;
     while (ptr) {
-      SilcProtocol protocol;
-      SilcServerKEInternalContext *proto_ctx;
-
-      /* Create the connection to the remote end */
-      sock = silc_net_create_connection(ptr->port, ptr->host);
-      if (sock < 0) {
-       SILC_LOG_ERROR(("Could not connect to router"));
-       silc_schedule_stop();
-       return;
-      }
 
-      /* Set socket options */
-      silc_net_set_socket_nonblock(sock);
-      silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-      /* Create socket connection for the connection. Even though we
-        know that we are connecting to a router we will mark the socket
-        to be unknown connection until we have executed authentication
-        protocol. */
-      silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-      server->sockets[sock] = newsocket;
-      newsocket->hostname = ptr->host;
-      newsocket->port = ptr->port;
-
-      /* Allocate internal protocol context. This is sent as context
-        to the protocol. */
-      proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-      proto_ctx->server = context;
-      proto_ctx->sock = newsocket;
-      proto_ctx->rng = server->rng;
-      proto_ctx->responder = FALSE;
-      
-      /* Perform key exchange protocol. silc_server_connect_to_router_final
-        will be called after the protocol is finished. */
-      silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                         &protocol, proto_ctx,
-                         silc_server_connect_to_router_second);
-      newsocket->protocol = protocol;
-
-      /* Register a timeout task that will be executed if the protocol
-        is not executed within 60 seconds. For now, this is a hard coded 
-        limit. After 60 secs the connection will be closed if the key 
-        exchange protocol has not been executed. */
-      proto_ctx->timeout_task = 
-       silc_task_register(server->timeout_queue, sock, 
-                          silc_server_timeout_remote,
-                          context, 60, 0,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_LOW);
-
-      /* Register the connection for network input and output. This sets
-        that scheduler will listen for incoming packets for this connection 
-        and sets that outgoing packets may be sent to this connection as 
-        well. However, this doesn't set the scheduler for outgoing traffic,
-        it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-        later when outgoing data is available. */
-      SILC_REGISTER_CONNECTION_FOR_IO(sock);
-      
-      /* Run the protocol */
-      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+      SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
+                     ptr->initiator ? "Initiator" : "Responder",
+                     ptr->host, ptr->port));
+
+      if (ptr->initiator) {
+       /* Allocate connection object for hold connection specific stuff. */
+       sconn = silc_calloc(1, sizeof(*sconn));
+       sconn->server = server;
+       sconn->remote_host = ptr->host;
+       sconn->remote_port = ptr->port;
+
+       silc_task_register(server->timeout_queue, fd, 
+                          silc_server_connect_router,
+                          (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                          SILC_TASK_PRI_NORMAL);
+      }
 
       if (!ptr->next)
        return;
@@ -547,17 +569,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   /* There wasn't a configured router, we will continue but we don't
      have a connection to outside world.  We will be standalone server. */
   server->standalone = TRUE;
-
-  /* Add a task to the queue. This task receives new connections to the 
-     server. This task remains on the queue until the end of the program. */
-  if (silc_task_register(server->io_queue, fd, 
-                        silc_server_accept_new_connection,
-                        (void *)server, 0, 0, 
-                        SILC_TASK_FD,
-                        SILC_TASK_PRI_NORMAL) == NULL) {
-    silc_schedule_stop();
-    return;
-  }
 }
 
 /* Second part of connecting to router(s). Key exchange protocol has been
@@ -569,6 +580,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
+  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
   SilcSocketConnection sock = NULL;
   SilcServerConnAuthInternalContext *proto_ctx;
 
@@ -578,7 +590,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     if (ctx->packet)
-      silc_buffer_free(ctx->packet);
+      silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     if (ctx->dest_id)
@@ -594,6 +606,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
      is sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
+  proto_ctx->context = (void *)sconn;
   proto_ctx->sock = sock = server->sockets[fd];
   proto_ctx->ske = ctx->ske;      /* Save SKE object from previous protocol */
   proto_ctx->dest_id_type = ctx->dest_id_type;
@@ -626,7 +639,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
   sock->protocol = NULL;
 
@@ -662,9 +675,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcServerConnAuthInternalContext *ctx = 
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
+  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
   SilcSocketConnection sock = ctx->sock;
   SilcServerEntry id_entry;
-  SilcUnknownEntry conn_data;
   SilcBuffer packet;
   unsigned char *id_string;
 
@@ -672,33 +685,22 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
     /* Error occured during protocol */
-    silc_protocol_free(protocol);
-    if (ctx->packet)
-      silc_buffer_free(ctx->packet);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
-    silc_free(ctx);
-    sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
-    return;
+    goto out;
   }
 
   /* Add a task to the queue. This task receives new connections to the 
      server. This task remains on the queue until the end of the program. */
   if (!server->listenning) {
-    if (silc_task_register(server->io_queue, server->sock, 
-                          silc_server_accept_new_connection,
-                          (void *)server, 0, 0, 
-                          SILC_TASK_FD,
-                          SILC_TASK_PRI_NORMAL) == NULL) {
-      silc_schedule_stop();
-      return;
-    } else {
-      server->listenning = TRUE;
-    }
+    silc_task_register(server->io_queue, server->sock, 
+                      silc_server_accept_new_connection,
+                      (void *)server, 0, 0, 
+                      SILC_TASK_FD,
+                      SILC_TASK_PRI_NORMAL);
+    server->listenning = TRUE;
   }
 
   /* Send NEW_SERVER packet to the router. We will become registered
@@ -725,28 +727,33 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add the connected router to local server list */
   server->standalone = FALSE;
-  conn_data = (SilcUnknownEntry)sock->user_data;
-  id_entry =
-    silc_idlist_add_server(server->local_list, 
-                          sock->hostname ? sock->hostname : sock->ip,
-                          SILC_ROUTER, ctx->dest_id, NULL,
-                          conn_data->send_key, conn_data->receive_key,
-                          conn_data->pkcs, conn_data->hmac, NULL, sock);
-  if (id_entry) {
-    id_entry->hmac_key = conn_data->hmac_key;
-    id_entry->hmac_key_len = conn_data->hmac_key_len;
-    sock->user_data = (void *)id_entry;
-    sock->type = SILC_SOCKET_TYPE_ROUTER;
-    server->id_entry->router = id_entry;
+  id_entry = silc_idlist_add_server(server->local_list, sock->hostname,
+                                   SILC_ROUTER, ctx->dest_id, NULL, sock);
+  if (!id_entry) {
+    if (ctx->dest_id)
+      silc_free(ctx->dest_id);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Authentication failed");
+    goto out;
   }
-    
-  /* Free the temporary connection data context from key exchange */
-  silc_free(conn_data);
+
+  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
+  silc_free(sock->user_data);
+  sock->user_data = (void *)id_entry;
+  sock->type = SILC_SOCKET_TYPE_ROUTER;
+  server->id_entry->router = id_entry;
+  server->router = id_entry;
+  server->router->data.registered = TRUE;
+
+ out:
+  /* Free the temporary connection data context */
+  if (sconn)
+    silc_free(sconn);
 
   /* Free the protocol object */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
   silc_free(ctx);
@@ -765,9 +772,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   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;
   }
 
@@ -778,6 +788,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
       /*silc_server_send_notify("Server is full, trying to redirect..."); */
     } else {
       SILC_LOG_ERROR(("Refusing connection, server is full"));
+      server->stat.conn_failures++;
     }
     return;
   }
@@ -794,13 +805,16 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   /* XXX This MUST be done async as this will block the entire server
      process. Either we have to do our own resolver stuff or in the future
      we can use threads. */
-  /* Perform mandatory name and address lookups for the remote host. */
+  /* Perform name and address lookups for the remote host. */
   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if (!newsocket->ip || !newsocket->hostname) {
-    SILC_LOG_DEBUG(("IP lookup/DNS lookup failed"));
-    SILC_LOG_ERROR(("IP lookup/DNS lookup failed"));
+  if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+      !newsocket->ip) {
+    SILC_LOG_ERROR(("IP/DNS lookup failed"));
+    server->stat.conn_failures++;
     return;
   }
+  if (!newsocket->hostname)
+    newsocket->hostname = strdup(newsocket->ip);
 
   SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
                 newsocket->ip));
@@ -817,6 +831,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      protocol but will not start it yet. The connector will be the
      initiator of the protocol thus we will wait for initiation from 
      there before we start the protocol. */
+  server->stat.auth_attempts++;
   silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
                      &newsocket->protocol, proto_ctx, 
                      silc_server_accept_new_connection_second);
@@ -862,15 +877,17 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     if (ctx->packet)
-      silc_buffer_free(ctx->packet);
+      silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    sock->protocol = NULL;
+    if (sock)
+      sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
+    server->stat.auth_failures++;
     return;
   }
 
@@ -887,7 +904,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
   sock->protocol = NULL;
 
@@ -921,6 +938,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
+  void *id_entry = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -928,15 +946,17 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     if (ctx->packet)
-      silc_buffer_free(ctx->packet);
+      silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    sock->protocol = NULL;
+    if (sock)
+      sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
+    server->stat.auth_failures++;
     return;
   }
 
@@ -945,7 +965,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry client;
-      SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
 
       SILC_LOG_DEBUG(("Remote host is client"));
       SILC_LOG_INFO(("Connection from %s (%s) is client", sock->hostname,
@@ -954,32 +973,27 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       /* Add the client to the client ID cache. The nickname and Client ID
         and other information is created after we have received NEW_CLIENT
         packet from client. */
-      client = 
-       silc_idlist_add_client(server->local_list, NULL, NULL, NULL, NULL,
-                              NULL, conn_data->send_key, 
-                              conn_data->receive_key, conn_data->pkcs,
-                              conn_data->hmac, NULL, sock);
+      client = silc_idlist_add_client(server->local_list, 
+                                     NULL, NULL, NULL, NULL, NULL, sock);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
-       silc_free(conn_data);
+       silc_free(sock->user_data);
        break;
       }
 
-      client->hmac_key = conn_data->hmac_key;
-      client->hmac_key_len = conn_data->hmac_key_len;
-      
-      /* Free the temporary connection data context from key exchange */
-      silc_free(conn_data);
+      /* Statistics */
+      server->stat.my_clients++;
+      server->stat.clients++;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_clients++;
 
-      /* Add to sockets internal pointer for fast referencing */
-      sock->user_data = (void *)client;
+      id_entry = (void *)client;
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry new_server;
-      SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
 
       SILC_LOG_DEBUG(("Remote host is %s", 
                      sock->type == SILC_SOCKET_TYPE_SERVER ? 
@@ -990,29 +1004,29 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       /* Add the server into server cache. The server name and Server ID
         is updated after we have received NEW_SERVER packet from the
-        server. */
+        server. We mark ourselves as router for this server if we really
+        are router. */
       new_server = 
        silc_idlist_add_server(server->local_list, NULL,
                               sock->type == SILC_SOCKET_TYPE_SERVER ?
-                              SILC_SERVER : SILC_ROUTER, NULL, NULL,
-                              conn_data->send_key, conn_data->receive_key,
-                              conn_data->pkcs, conn_data->hmac, NULL, sock);
+                              SILC_SERVER : SILC_ROUTER, NULL, 
+                              sock->type == SILC_SOCKET_TYPE_SERVER ?
+                              server->id_entry : NULL, sock);
       if (!new_server) {
        SILC_LOG_ERROR(("Could not add new server to cache"));
-       silc_free(conn_data);
+       silc_free(sock->user_data);
        break;
       }
-      
-      new_server->registered = TRUE;
-      new_server->hmac_key = conn_data->hmac_key;
-      new_server->hmac_key_len = conn_data->hmac_key_len;
-      
-      /* Free the temporary connection data context from protocols */
-      silc_free(conn_data);
 
-      /* Add to sockets internal pointer for fast referencing */
-      sock->user_data = (void *)new_server;
+      /* Statistics */
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.my_servers++;
+      else
+       server->stat.my_routers++;
+      server->stat.servers++;
 
+      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,
@@ -1020,6 +1034,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
        SILC_LOG_DEBUG(("We are not standalone server anymore"));
        server->standalone = FALSE;
+       if (!server->id_entry->router) {
+         server->id_entry->router = id_entry;
+         server->router = id_entry;
+       }
       }
       break;
     }
@@ -1027,12 +1045,20 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     break;
   }
 
+  /* Add the common data structure to the ID entry. */
+  if (id_entry)
+    silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
+      
+  /* Add to sockets internal pointer for fast referencing */
+  silc_free(sock->user_data);
+  sock->user_data = id_entry;
+
   /* Connection has been fully established now. Everything is ok. */
   SILC_LOG_DEBUG(("New connection authenticated"));
 
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
   if (ctx->dest_id)
@@ -1041,14 +1067,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   sock->protocol = NULL;
 }
 
-typedef struct {
-  SilcPacketContext *packetdata;
-  SilcServer server;
-  SilcSocketConnection sock;
-  SilcCipher cipher;
-  SilcHmac hmac;
-} SilcServerInternalPacket;
-
 /* This function is used to read packets from network and send packets to
    network. This is usually a generic task. */
 
@@ -1056,30 +1074,34 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 {
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = server->sockets[fd];
-  int ret, packetlen, paddedlen;
+  SilcIDListData idata;
+  SilcCipher cipher = NULL;
+  SilcHmac hmac = NULL;
+  int ret;
+
+  if (!sock)
+    return;
 
   SILC_LOG_DEBUG(("Processing packet"));
 
   /* Packet sending */
+
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
+    server->stat.packets_sent++;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    /* Write the packet out to the connection */
-    ret = silc_packet_write(fd, sock->outbuf);
+    ret = silc_server_packet_send_real(server, sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
-    
-    /* Error */
-    if (ret == -1)
-      SILC_LOG_ERROR(("Could not write, packet dropped"));
 
+    if (ret == -1)
+      return;
+    
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
        available for this connection it will be set for output as well. 
@@ -1092,547 +1114,172 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   }
 
   /* Packet receiving */
-  if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
-
-    /* Allocate the incoming data buffer if not done already. */
-    if (!sock->inbuf)
-      sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
 
-    /* Read some data from connection */
-    ret = silc_packet_read(fd, sock->inbuf);
-    
-    /* If returned -2 data was not available now, will read it later. */
-    if (ret == -2)
-      return;
-    
-    /* Error */
-    if (ret == -1) {
-      SILC_LOG_ERROR(("Could not read, packet dropped"));
-      return;
-    }
+  /* Read some data from connection */
+  ret = silc_packet_receive(sock);
+  if (ret < 0)
+    return;
     
-    /* EOF */
-    if (ret == 0) {
-      SILC_LOG_DEBUG(("Read EOF"));
-      
-      /* If connection is disconnecting already we will finally
-        close the connection */
-      if (SILC_IS_DISCONNECTING(sock)) {
-       if (sock->user_data)
-         silc_server_free_sock_user_data(server, sock);
-       silc_server_close_connection(server, sock);
-       return;
-      }
+  /* EOF */
+  if (ret == 0) {
+    SILC_LOG_DEBUG(("Read EOF"));
       
-      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
-
+    /* If connection is disconnecting already we will finally
+       close the connection */
+    if (SILC_IS_DISCONNECTING(sock)) {
       if (sock->user_data)
-         silc_server_free_sock_user_data(server, sock);
+       silc_server_free_sock_user_data(server, sock);
       silc_server_close_connection(server, sock);
       return;
     }
-
-    /* If connection is disconnecting or disconnected we will ignore
-       what we read. */
-    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
-      SILC_LOG_DEBUG(("Ignoring read data from invalid connection"));
-      return;
-    }
-
-    /* Check whether we received a whole packet. If reading went without
-       errors we either read a whole packet or the read packet is 
-       incorrect and will be dropped. */
-    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-    if (sock->inbuf->len < paddedlen || (packetlen < SILC_PACKET_MIN_LEN)) {
-      SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-      silc_buffer_clear(sock->inbuf);
-      silc_server_disconnect_remote(server, sock, "Incorrect packet");
-      return;
-    }
-
-    /* Decrypt a packet coming from client. */
-    if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientEntry clnt = (SilcClientEntry)sock->user_data;
-      SilcServerInternalPacket *packet;
-      int mac_len = 0;
-      
-      if (clnt->hmac)
-       mac_len = clnt->hmac->hash->hash->hash_len;
-
-      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
-       /* Received possibly many packets at once */
-
-       while(sock->inbuf->len > 0) {
-         SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-         if (sock->inbuf->len < paddedlen) {
-           SILC_LOG_DEBUG(("Receive incorrect packet, dropped"));
-           return;
-         }
-
-         paddedlen += 2;
-         packet = silc_calloc(1, sizeof(*packet));
-         packet->server = server;
-         packet->sock = sock;
-         packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-         packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
-         silc_buffer_pull_tail(packet->packetdata->buffer, 
-                               SILC_BUFFER_END(packet->packetdata->buffer));
-         silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, 
-                         paddedlen + mac_len);
-         if (clnt) {
-           packet->cipher = clnt->receive_key;
-           packet->hmac = clnt->hmac;
-         }
-
-         SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                           packet->packetdata->buffer->len),
-                          packet->packetdata->buffer->data, 
-                          packet->packetdata->buffer->len);
-
-         /* Parse the packet with timeout */
-         silc_task_register(server->timeout_queue, fd, 
-                            silc_server_packet_parse,
-                            (void *)packet, 0, 100000, 
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
-
-         /* Pull the packet from inbuf thus we'll get the next one
-            in the inbuf. */
-         silc_buffer_pull(sock->inbuf, paddedlen);
-         if (clnt->hmac)
-           silc_buffer_pull(sock->inbuf, mac_len);
-       }
-       silc_buffer_clear(sock->inbuf);
-       return;
-      } else {
-       SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
-                        sock->inbuf->data, sock->inbuf->len);
-       
-       SILC_LOG_DEBUG(("Packet from client, length %d", paddedlen));
-       
-       packet = silc_calloc(1, sizeof(*packet));
-       packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-       packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
-       packet->server = server;
-       packet->sock = sock;
-       if (clnt) {
-         packet->cipher = clnt->receive_key;
-         packet->hmac = clnt->hmac;
-       }
-       silc_buffer_clear(sock->inbuf);
-       
-       /* The packet is ready to be parsed now. However, this is a client 
-          connection so we will parse the packet with timeout. */
-       silc_task_register(server->timeout_queue, fd, 
-                          silc_server_packet_parse,
-                          (void *)packet, 0, 100000, 
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_NORMAL);
-       return;
-      }
-    }
-    
-    /* Decrypt a packet coming from server connection */
-    if (sock->type == SILC_SOCKET_TYPE_SERVER ||
-       sock->type == SILC_SOCKET_TYPE_ROUTER) {
-      SilcServerEntry srvr = (SilcServerEntry)sock->user_data;
-      SilcServerInternalPacket *packet;
-      int mac_len = 0;
-      
-      if (srvr->hmac)
-       mac_len = srvr->hmac->hash->hash->hash_len;
-
-      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
-       /* Received possibly many packets at once */
-
-       while(sock->inbuf->len > 0) {
-         SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-         if (sock->inbuf->len < paddedlen) {
-           SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-           return;
-         }
-
-         paddedlen += 2;
-         packet = silc_calloc(1, sizeof(*packet));
-         packet->server = server;
-         packet->sock = sock;
-         packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-         packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
-         silc_buffer_pull_tail(packet->packetdata->buffer, 
-                               SILC_BUFFER_END(packet->packetdata->buffer));
-         silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, 
-                         paddedlen + mac_len);
-         if (srvr) {
-           packet->cipher = srvr->receive_key;
-           packet->hmac = srvr->hmac;
-         }
-
-         SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                           packet->packetdata->buffer->len),
-                          packet->packetdata->buffer->data, 
-                          packet->packetdata->buffer->len);
-
-         SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", 
-                         srvr->server_type == SILC_SERVER ? 
-                         "server" : "router",
-                         srvr->server_name, paddedlen));
-       
-         /* Parse it real soon as the packet is from server. */
-         silc_task_register(server->timeout_queue, fd, 
-                            silc_server_packet_parse,
-                            (void *)packet, 0, 1, 
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
-
-         /* Pull the packet from inbuf thus we'll get the next one
-            in the inbuf. */
-         silc_buffer_pull(sock->inbuf, paddedlen);
-         if (srvr->hmac)
-           silc_buffer_pull(sock->inbuf, mac_len);
-       }
-       silc_buffer_clear(sock->inbuf);
-       return;
-      } else {
-
-       SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
-                        sock->inbuf->data, sock->inbuf->len);
-       
-       SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", 
-                       srvr->server_type == SILC_SERVER ? 
-                       "server" : "router",
-                       srvr->server_name, paddedlen));
-       
-       packet = silc_calloc(1, sizeof(*packet));
-       packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-       packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
-       packet->server = server;
-       packet->sock = sock;
-       if (srvr) {
-         packet->cipher = srvr->receive_key;
-         packet->hmac = srvr->hmac;
-       }
-       silc_buffer_clear(sock->inbuf);
-       
-       /* The packet is ready to be parsed now. However, this is a client 
-          connection so we will parse the packet with timeout. */
-       silc_task_register(server->timeout_queue, fd, 
-                          silc_server_packet_parse,
-                          (void *)packet, 0, 1, 
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_NORMAL);
-       return;
-      }
-    }
-
-    /* Decrypt a packet coming from client. */
-    if (sock->type == SILC_SOCKET_TYPE_UNKNOWN) {
-      SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
-      SilcServerInternalPacket *packet;
       
-      SILC_LOG_HEXDUMP(("Incoming packet, len %d", sock->inbuf->len),
-                      sock->inbuf->data, sock->inbuf->len);
-
-      SILC_LOG_DEBUG(("Packet from unknown connection, length %d", 
-                     paddedlen));
-
-      packet = silc_calloc(1, sizeof(*packet));
-      packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-      packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
-      packet->server = server;
-      packet->sock = sock;
-      if (conn_data) {
-       packet->cipher = conn_data->receive_key;
-       packet->hmac = conn_data->hmac;
-      }
-
-      silc_buffer_clear(sock->inbuf);
+    SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
 
-      /* The packet is ready to be parsed now. However, this is unknown 
-        connection so we will parse the packet with timeout. */
-      silc_task_register(server->timeout_queue, fd, 
-                        silc_server_packet_parse,
-                        (void *)packet, 0, 100000, 
+    /* If the closed connection was our primary router connection the
+       start re-connecting phase. */
+    if (!server->standalone && server->server_type == SILC_SERVER && 
+       sock == server->router->connection)
+      silc_task_register(server->timeout_queue, 0, 
+                        silc_server_connect_to_router,
+                        context, 0, 500000,
                         SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
-      return;
-    }
+
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock);
+    silc_server_close_connection(server, sock);
+    return;
+  }
+
+  /* If connection is disconnecting or disconnected we will ignore
+     what we read. */
+  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+    SILC_LOG_DEBUG(("Ignoring read data from invalid connection"));
+    return;
   }
 
-  SILC_LOG_ERROR(("Weird, nothing happened - ignoring"));
+  server->stat.packets_received++;
+
+  /* Get keys and stuff from ID entry */
+  idata = (SilcIDListData)sock->user_data;
+  if (idata) {
+    idata->last_receive = time(NULL);
+    cipher = idata->receive_key;
+    hmac = idata->hmac;
+  }
+  /* Process the packet. This will call the parser that will then
+     decrypt and parse the packet. */
+  silc_packet_receive_process(sock, cipher, hmac, silc_server_packet_parse, 
+                             server);
 }
 
-/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
-   after packet has been totally decrypted and parsed. */
+/* Parses whole packet, received earlier. */
 
-static int silc_server_packet_check_mac(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer buffer)
+SILC_TASK_CALLBACK(silc_server_packet_parse_real)
 {
-  SilcHmac hmac = NULL;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned int mac_len = 0;
+  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
+  SilcServer server = (SilcServer)parse_ctx->context;
+  SilcSocketConnection sock = parse_ctx->sock;
+  SilcPacketContext *packet = parse_ctx->packet;
+  int ret;
 
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      hmac = ((SilcClientEntry)sock->user_data)->hmac;
-      hmac_key = ((SilcClientEntry)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcClientEntry)sock->user_data)->hmac_key_len;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      hmac = ((SilcServerEntry)sock->user_data)->hmac;
-      hmac_key = ((SilcServerEntry)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcServerEntry)sock->user_data)->hmac_key_len;
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      hmac = ((SilcUnknownEntry)sock->user_data)->hmac;
-      hmac_key = ((SilcUnknownEntry)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcUnknownEntry)sock->user_data)->hmac_key_len;
-    }
-  }
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Check MAC */
-  if (hmac) {
-    int headlen = buffer->data - buffer->head;
-    unsigned char *packet_mac, mac[32];
-    
-    SILC_LOG_DEBUG(("Verifying MAC"));
+  /* Decrypt the received packet */
+  ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac, 
+                           packet->buffer, packet);
+  if (ret < 0)
+    goto out;
 
-    mac_len = hmac->hash->hash->hash_len;
+  if (ret == 0) {
+    /* Parse the packet. Packet type is returned. */
+    ret = silc_packet_parse(packet);
+  } else {
+    /* Parse the packet header in special way as this is "special"
+       packet type. */
+    ret = silc_packet_parse_special(packet);
+  }
 
-    silc_buffer_push(buffer, headlen);
-    
-    /* Take mac from packet */
-    packet_mac = buffer->tail;
-    
-    /* Make MAC and compare */
-    memset(mac, 0, sizeof(mac));
-    silc_hmac_make_with_key(hmac, 
-                           buffer->data, buffer->len,
-                           hmac_key, hmac_key_len, mac);
-#if 0
-    SILC_LOG_HEXDUMP(("PMAC"), packet_mac, mac_len);
-    SILC_LOG_HEXDUMP(("CMAC"), mac, mac_len);
-#endif
-    if (memcmp(mac, packet_mac, mac_len)) {
-      SILC_LOG_DEBUG(("MAC failed"));
-      return FALSE;
+  if (ret == SILC_PACKET_NONE)
+    goto out;
+
+  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 && 
+       sock->type != SILC_SOCKET_TYPE_CLIENT &&
+       SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+      
+      /* Route the packet to fastest route for the destination ID */
+      void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
+                               packet->dst_id_type);
+      if (!id)
+       goto out;
+      silc_server_packet_route(server,
+                              silc_server_route_get(server, id,
+                                                    packet->dst_id_type),
+                              packet);
+      silc_free(id);
+      goto out;
     }
     
-    SILC_LOG_DEBUG(("MAC is Ok"));
-    memset(mac, 0, sizeof(mac));
-
-    silc_buffer_pull(buffer, headlen);
+    /* 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) {
+      silc_server_packet_broadcast(server, server->router->connection, packet);
+    }
   }
-  
-  return TRUE;
+
+  /* Parse the incoming packet type */
+  silc_server_packet_parse_type(server, sock, packet);
+
+ out:
+  silc_buffer_clear(sock->inbuf);
+  silc_packet_context_free(packet);
+  silc_free(parse_ctx);
 }
 
-/* Decrypts rest of the packet (after decrypting just the SILC header).
-   After calling this function the packet is ready to be parsed by calling 
-   silc_packet_parse. */
+/* Parser callback called by silc_packet_receive_process. This merely
+   registers timeout that will handle the actual parsing when appropriate. */
 
-static int silc_server_packet_decrypt_rest(SilcServer server, 
-                                          SilcSocketConnection sock,
-                                          SilcBuffer buffer)
+void silc_server_packet_parse(SilcPacketParserContext *parser_context)
 {
-  SilcCipher session_key = NULL;
-  SilcHmac hmac = NULL;
-  unsigned int mac_len = 0;
+  SilcServer server = (SilcServer)parser_context->context;
+  SilcSocketConnection sock = parser_context->sock;
 
-  switch(sock->type) {
+  switch (sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      session_key = ((SilcClientEntry)sock->user_data)->receive_key;
-      hmac = ((SilcClientEntry)sock->user_data)->hmac;
-    }
+  case SILC_SOCKET_TYPE_UNKNOWN:
+    /* Parse the packet with timeout */
+    silc_task_register(server->timeout_queue, sock->sock,
+                      silc_server_packet_parse_real,
+                      (void *)parser_context, 0, 100000,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_NORMAL);
     break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      session_key = ((SilcServerEntry)sock->user_data)->receive_key;
-      hmac = ((SilcServerEntry)sock->user_data)->hmac;
-    }
+    /* Packets from servers are parsed as soon as possible */
+    silc_task_register(server->timeout_queue, sock->sock,
+                      silc_server_packet_parse_real,
+                      (void *)parser_context, 0, 1,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_NORMAL);
     break;
   default:
-    if (sock->user_data) {
-      session_key = ((SilcUnknownEntry)sock->user_data)->receive_key;
-      hmac = ((SilcUnknownEntry)sock->user_data)->hmac;
-    }
+    return;
   }
-  
-  /* Decrypt */
-  if (session_key) {
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      mac_len = hmac->hash->hash->hash_len;
-      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, mac_len);
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-
-    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
+}
 
-    /* Decrypt rest of the packet */
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    silc_packet_decrypt(session_key, buffer, buffer->len);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
-                    buffer->data, buffer->len);
-  }
-
-  return TRUE;
-}
-
-/* Decrypts rest of the SILC Packet header that has been decrypted partly
-   already. This decrypts the padding of the packet also.  After calling 
-   this function the packet is ready to be parsed by calling function 
-   silc_packet_parse. */
-
-static int silc_server_packet_decrypt_rest_special(SilcServer server, 
-                                                  SilcSocketConnection sock,
-                                                  SilcBuffer buffer)
-{
-  SilcCipher session_key = NULL;
-  SilcHmac hmac = NULL;
-  unsigned int mac_len = 0;
-
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      session_key = ((SilcClientEntry)sock->user_data)->receive_key;
-      hmac = ((SilcClientEntry)sock->user_data)->hmac;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      session_key = ((SilcServerEntry)sock->user_data)->receive_key;
-      hmac = ((SilcServerEntry)sock->user_data)->hmac;
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      session_key = ((SilcUnknownEntry)sock->user_data)->receive_key;
-      hmac = ((SilcUnknownEntry)sock->user_data)->hmac;
-    }
-  }
-  
-  /* Decrypt rest of the header plus padding */
-  if (session_key) {
-    unsigned short truelen, len1, len2, padlen;
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      mac_len = hmac->hash->hash->hash_len;
-      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, mac_len);
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-  
-    SILC_LOG_DEBUG(("Decrypting rest of the header"));
-
-    SILC_GET16_MSB(len1, &buffer->data[4]);
-    SILC_GET16_MSB(len2, &buffer->data[6]);
-
-    truelen = SILC_PACKET_HEADER_LEN + len1 + len2;
-    padlen = SILC_PACKET_PADLEN(truelen);
-    len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    silc_packet_decrypt(session_key, buffer, len1);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-  }
-
-  return TRUE;
-}
-
-/* Parses whole packet, received earlier. This packet is usually received
-   from client. */
-
-SILC_TASK_CALLBACK(silc_server_packet_parse)
-{
-  SilcServerInternalPacket *packet = (SilcServerInternalPacket *)context;
-  SilcServer server = packet->server;
-  SilcSocketConnection sock = packet->sock;
-  SilcBuffer buffer = packet->packetdata->buffer;
-  int ret;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Decrypt start of the packet header */
-  if (packet->cipher)
-    silc_packet_decrypt(packet->cipher, buffer, SILC_PACKET_MIN_HEADER_LEN);
-
-  /* If the packet type is not any special type lets decrypt rest
-     of the packet here. */
-  if (buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE &&
-      buffer->data[3] != SILC_PACKET_PRIVATE_MESSAGE) {
-  normal:
-    /* Normal packet, decrypt rest of the packet */
-    if (!silc_server_packet_decrypt_rest(server, sock, buffer))
-      goto out;
-
-    /* Parse the packet. Packet type is returned. */
-    ret = silc_packet_parse(packet->packetdata);
-    if (ret == SILC_PACKET_NONE)
-      goto out;
-
-    /* Check MAC */
-    if (!silc_server_packet_check_mac(server, sock, buffer))
-      goto out;
-  } else {
-    /* If private message key is not set for private message it is
-       handled as normal packet. Go back up. */
-    if (buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-       !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
-      goto normal;
-
-    /* Packet requires special handling, decrypt rest of the header.
-       This only decrypts. This does not do any MAC checking, it must
-       be done individually later when doing the special processing. */
-    silc_server_packet_decrypt_rest_special(server, sock, buffer);
-
-    /* Parse the packet header in special way as this is "special"
-       packet type. */
-    ret = silc_packet_parse_special(packet->packetdata);
-    if (ret == SILC_PACKET_NONE)
-      goto out;
-  }
-  
-  /* Parse the incoming packet type */
-  silc_server_packet_parse_type(server, sock, packet->packetdata);
-
- out:
-  silc_buffer_clear(sock->inbuf);
-  //  silc_buffer_free(packetdata->packetdata->buffer);
-  silc_free(packet->packetdata);
-  silc_free(packet);
-}
-
-/* Parses the packet type and calls what ever routines the packet type
-   requires. This is done for all incoming packets. */
+/* Parses the packet type and calls what ever routines the packet type
+   requires. This is done for all incoming packets. */
 
 void silc_server_packet_parse_type(SilcServer server, 
                                   SilcSocketConnection sock,
                                   SilcPacketContext *packet)
 {
-  SilcBuffer buffer = packet->buffer;
   SilcPacketType type = packet->type;
 
   SILC_LOG_DEBUG(("Parsing packet type %d", type));
@@ -1642,6 +1289,7 @@ void silc_server_packet_parse_type(SilcServer server,
   case SILC_PACKET_DISCONNECT:
     SILC_LOG_DEBUG(("Disconnect packet"));
     break;
+
   case SILC_PACKET_SUCCESS:
     /*
      * Success received for something. For now we can have only
@@ -1654,21 +1302,43 @@ void silc_server_packet_parse_type(SilcServer server,
                              sock->protocol, sock->sock, 0, 0);
     }
     break;
+
   case SILC_PACKET_FAILURE:
+    /*
+     * Failure received for something. For now we can have only
+     * one protocol for connection executing at once hence this
+     * failure message is for whatever protocol is executing currently.
+     */
     SILC_LOG_DEBUG(("Failure packet"));
+    if (sock->protocol) {
+      /* XXX Audit the failure type */
+      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+      sock->protocol->execute(server->timeout_queue, 0,
+                             sock->protocol, sock->sock, 0, 0);
+    }
     break;
+
   case SILC_PACKET_REJECT:
     SILC_LOG_DEBUG(("Reject packet"));
     return;
     break;
 
+  case SILC_PACKET_NOTIFY:
+    /*
+     * Received notify packet. Server can receive notify packets from
+     * router. Server then relays the notify messages to clients if needed.
+     */
+    SILC_LOG_DEBUG(("Notify packet"));
+    silc_server_notify(server, sock, packet);
+    break;
+
     /* 
      * Channel packets
      */
   case SILC_PACKET_CHANNEL_MESSAGE:
     /*
      * Received channel message. Channel messages are special packets
-     * (although probably most common ones) hence they are handled
+     * (although probably most common ones) thus they are handled
      * specially.
      */
     SILC_LOG_DEBUG(("Channel Message packet"));
@@ -1691,7 +1361,8 @@ void silc_server_packet_parse_type(SilcServer server,
      */
   case SILC_PACKET_COMMAND:
     /*
-     * Recived command. Allocate command context and execute the command.
+     * Recived command. Processes the command request and allocates the
+     * command context and calls the command.
      */
     SILC_LOG_DEBUG(("Command packet"));
     silc_server_command_process(server, sock, packet);
@@ -1699,13 +1370,12 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_COMMAND_REPLY:
     /*
-     * Received command reply packet. Servers never send commands thus
-     * they don't receive command reply packets either, except in cases
-     * where server has forwarded command packet coming from client. 
-     * This must be the case here or we will ignore the packet.
+     * Received command reply packet. Received command reply to command. It
+     * may be reply to command sent by us or reply to command sent by client
+     * that we've routed further.
      */
     SILC_LOG_DEBUG(("Command Reply packet"));
-    silc_server_packet_relay_command_reply(server, sock, packet);
+    silc_server_command_reply(server, sock, packet);
     break;
 
     /*
@@ -1721,6 +1391,9 @@ void silc_server_packet_parse_type(SilcServer server,
     break;
 
   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+    /*
+     * Private message key packet.
+     */
     break;
 
     /*
@@ -1734,7 +1407,7 @@ void silc_server_packet_parse_type(SilcServer server,
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1756,11 +1429,14 @@ void silc_server_packet_parse_type(SilcServer server,
        (SilcServerKEInternalContext *)sock->protocol->context;
 
       if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
+       silc_packet_context_free(proto_ctx->packet);
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
       proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1781,11 +1457,14 @@ void silc_server_packet_parse_type(SilcServer server,
        (SilcServerKEInternalContext *)sock->protocol->context;
 
       if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
+       silc_packet_context_free(proto_ctx->packet);
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
       proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1798,9 +1477,14 @@ void silc_server_packet_parse_type(SilcServer server,
     break;
 
   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
-    /* If we receive this packet we will send to the other end information
-       about our mandatory authentication method for the connection. 
-       This packet maybe received at any time. */
+    /*
+     * Connection authentication request packet. When we receive this packet
+     * we will send to the other end information about our mandatory
+     * authentication method for the connection. This packet maybe received
+     * at any time. 
+     */
+    SILC_LOG_DEBUG(("Connection authentication request packet"));
+    break;
 
     /*
      * Connection Authentication protocol packets
@@ -1815,7 +1499,7 @@ void silc_server_packet_parse_type(SilcServer server,
       SilcServerConnAuthInternalContext *proto_ctx = 
        (SilcServerConnAuthInternalContext *)sock->protocol->context;
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1850,12 +1534,85 @@ void silc_server_packet_parse_type(SilcServer server,
   case SILC_PACKET_NEW_SERVER:
     /*
      * Received new server packet. This includes Server ID and some other
-     * information that we may save. This is after server as connected to us.
+     * information that we may save. This is received after server has 
+     * connected to us.
      */
     SILC_LOG_DEBUG(("New Server packet"));
     silc_server_new_server(server, sock, packet);
     break;
 
+  case SILC_PACKET_NEW_CHANNEL:
+    /*
+     * Received new channel packet. Information about new channel in the
+     * network are distributed using this packet.
+     */
+    SILC_LOG_DEBUG(("New Channel packet"));
+    silc_server_new_channel(server, sock, packet);
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_USER:
+    /*
+     * Received new channel user packet. Information about new users on a
+     * channel are distributed between routers using this packet.  The
+     * router receiving this will redistribute it and also sent JOIN notify
+     * to local clients on the same channel. Normal server sends JOIN notify
+     * to its local clients on the channel.
+     */
+    SILC_LOG_DEBUG(("New Channel User packet"));
+    silc_server_new_channel_user(server, sock, packet);
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_LIST:
+    /*
+     * List of new channel packets received. This is usually received when
+     * existing server or router connects to us and distributes information
+     * of all channels it has.
+     */
+    break;
+
+  case SILC_PACKET_NEW_CHANNEL_USER_LIST:
+    /*
+     * List of new channel user packets received. This is usually received
+     * when existing server or router connects to us and distributes 
+     * information of all channel users it has.
+     */
+    break;
+
+  case SILC_PACKET_REPLACE_ID:
+    /*
+     * Received replace ID packet. This sends the old ID that is to be
+     * replaced with the new one included into the packet. Client must not
+     * send this packet.
+     */
+    SILC_LOG_DEBUG(("Replace ID packet"));
+    silc_server_replace_id(server, sock, packet);
+    break;
+
+  case SILC_PACKET_REMOVE_ID:
+    /*
+     * Received remove ID Packet. 
+     */
+    SILC_LOG_DEBUG(("Remove ID packet"));
+    silc_server_remove_id(server, sock, packet);
+    break;
+
+  case SILC_PACKET_REMOVE_CHANNEL_USER:
+    /*
+     * Received packet to remove user from a channel. Routers notify other
+     * routers about a user leaving a channel.
+     */
+    SILC_LOG_DEBUG(("Remove Channel User packet"));
+    silc_server_remove_channel_user(server, sock, packet);
+    break;
+
+  case SILC_PACKET_SET_MODE:
+    /*
+     * Received packet to set the mode of channel or client's channel mode.
+     */
+    SILC_LOG_DEBUG(("Set Mode packet"));
+    silc_server_set_mode(server, sock, packet);
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1863,1169 +1620,297 @@ void silc_server_packet_parse_type(SilcServer server,
   
 }
 
-/* Internal routine that sends packet or marks packet to be sent. This
-   is used directly only in special cases. Normal cases should use
-   silc_server_packet_send. Returns < 0 error. */
+/* Closes connection to socket connection */
 
-static int silc_server_packet_send_real(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       int force_send)
+void silc_server_close_connection(SilcServer server,
+                                 SilcSocketConnection sock)
 {
-  /* Send now if forced to do so */
-  if (force_send == TRUE) {
-    int ret;
-    SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
-    ret = silc_packet_write(sock->sock, sock->outbuf);
-
-    silc_buffer_clear(sock->outbuf);
-
-    if (ret == -1)
-      SILC_LOG_ERROR(("Could not write, packet dropped"));
-    if (ret != -2) {
-      silc_buffer_clear(sock->outbuf);
-      return ret;
-    }
-
-    SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
-  }  
-
-  SILC_LOG_DEBUG(("Packet in queue"));
+  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
 
-  /* Mark that there is some outgoing data available for this connection. 
-     This call sets the connection both for input and output (the input
-     is set always and this call keeps the input setting, actually). 
-     Actual data sending is performed by silc_server_packet_process. */
-  SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock);
+  /* We won't listen for this connection anymore */
+  silc_schedule_unset_listen_fd(sock->sock);
 
-  /* Mark to socket that data is pending in outgoing buffer. This flag
-     is needed if new data is added to the buffer before the earlier
-     put data is sent to the network. */
-  SILC_SET_OUTBUF_PENDING(sock);
+  /* Unregister all tasks */
+  silc_task_unregister_by_fd(server->io_queue, sock->sock);
+  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
 
-  return 0;
+  /* Close the actual connection */
+  silc_net_close_connection(sock->sock);
+  server->sockets[sock->sock] = NULL;
+  silc_socket_free(sock);
 }
 
-/* Prepare outgoing data buffer for packet sending. This is internal
-   routine and must always be called before sending any packets out. */
+/* Sends disconnect message to remote connection and disconnects the 
+   connection. */
 
-static void silc_server_packet_send_prepare(SilcServer server, 
-                                           SilcSocketConnection sock,
-                                           unsigned int header_len,
-                                           unsigned int padlen,
-                                           unsigned int data_len)
+void silc_server_disconnect_remote(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  const char *fmt, ...)
 {
-  int totlen, oldlen;
+  va_list ap;
+  unsigned char buf[4096];
 
-  totlen = header_len + padlen + data_len;
+  if (!sock)
+    return;
 
-  /* Prepare the outgoing buffer for packet sending. */
-  if (!sock->outbuf) {
-    /* Allocate new buffer. This is done only once per connection. */
-    SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
-    
-    sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
-    silc_buffer_pull_tail(sock->outbuf, totlen);
-    silc_buffer_pull(sock->outbuf, header_len + padlen);
-  } else {
-    if (SILC_IS_OUTBUF_PENDING(sock)) {
-      /* There is some pending data in the buffer. */
+  memset(buf, 0, sizeof(buf));
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  va_end(ap);
 
-      if ((sock->outbuf->end - sock->outbuf->tail) < data_len) {
-       SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
-       /* XXX: not done yet */
-      }
-      oldlen = sock->outbuf->len;
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
-    } else {
-      /* Buffer is free for use */
-      silc_buffer_clear(sock->outbuf);
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen);
-    }
-  }
+  SILC_LOG_DEBUG(("Disconnecting remote host"));
+
+  /* Notify remote end that the conversation is over. The notify message
+     is tried to be sent immediately. */
+  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
+                         buf, strlen(buf), TRUE);
+
+  /* Mark the connection to be disconnected */
+  SILC_SET_DISCONNECTED(sock);
+  silc_server_close_connection(server, sock);
 }
 
-/* Assembles a new packet to be sent out to network. This doesn't actually
-   send the packet but creates the packet and fills the outgoing data
-   buffer and marks the packet ready to be sent to network. However, If 
-   argument force_send is TRUE the packet is sent immediately and not put 
-   to queue. Normal case is that the packet is not sent immediately. */
-
-void silc_server_packet_send(SilcServer server,
-                            SilcSocketConnection sock, 
-                            SilcPacketType type, 
-                            SilcPacketFlags flags,
-                            unsigned char *data, 
-                            unsigned int data_len,
-                            int force_send)
+/* Free's user_data pointer from socket connection object. This also sends
+   appropriate notify packets to the network to inform about leaving
+   entities. */
+
+void silc_server_free_sock_user_data(SilcServer server, 
+                                    SilcSocketConnection sock)
 {
-  void *dst_id = NULL;
-  SilcIdType dst_id_type = SILC_ID_NONE;
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Get data used in the packet sending, keys and stuff */
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
-    if (((SilcClientEntry)sock->user_data)->id) {
-      dst_id = ((SilcClientEntry)sock->user_data)->id;
-      dst_id_type = SILC_ID_CLIENT;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (((SilcServerEntry)sock->user_data)->id) {
-      dst_id = ((SilcServerEntry)sock->user_data)->id;
-      dst_id_type = SILC_ID_SERVER;
-    }
-    break;
-  default:
-    break;
-  }
+    {
+      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
 
-  silc_server_packet_send_dest(server, sock, type, flags, dst_id,
-                              dst_id_type, data, data_len, force_send);
-}
+      /* Remove client from all channels */
+      silc_server_remove_from_channels(server, sock, user_data);
 
-/* Assembles a new packet to be sent out to network. This doesn't actually
-   send the packet but creates the packet and fills the outgoing data
-   buffer and marks the packet ready to be sent to network. However, If 
-   argument force_send is TRUE the packet is sent immediately and not put 
-   to queue. Normal case is that the packet is not sent immediately. 
-   Destination information is sent as argument for this function. */
-
-void silc_server_packet_send_dest(SilcServer server,
-                                 SilcSocketConnection sock, 
-                                 SilcPacketType type, 
-                                 SilcPacketFlags flags,
-                                 void *dst_id,
-                                 SilcIdType dst_id_type,
-                                 unsigned char *data, 
-                                 unsigned int data_len,
-                                 int force_send)
-{
-  SilcPacketContext packetdata;
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  unsigned char *dst_id_data = NULL;
-  unsigned int dst_id_len = 0;
+      /* XXX must take some info to history before freeing */
 
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+      /* Send REMOVE_ID packet to routers. */
+      if (!server->standalone && server->router)
+       silc_server_send_remove_id(server, server->router->connection,
+                                  server->server_type == SILC_SERVER ?
+                                  FALSE : TRUE, user_data->id, 
+                                  SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      cipher = ((SilcClientEntry)sock->user_data)->send_key;
-      hmac = ((SilcClientEntry)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcClientEntry)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcClientEntry)sock->user_data)->hmac_key_len;
-      }
+      /* Free the client entry and everything in it */
+      silc_idlist_del_data(user_data);
+      silc_idlist_del_client(server->local_list, user_data);
+      server->stat.my_clients--;
+      server->stat.clients--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_clients--;
+      break;
     }
-    break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      cipher = ((SilcServerEntry)sock->user_data)->send_key;
-      hmac = ((SilcServerEntry)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcServerEntry)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcServerEntry)sock->user_data)->hmac_key_len;
-      }
+    {
+      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+
+      /* Send REMOVE_ID packet to routers. */
+      if (!server->standalone && server->router)
+       silc_server_send_remove_id(server, server->router->connection,
+                                  server->server_type == SILC_SERVER ?
+                                  FALSE : TRUE, user_data->id, 
+                                  SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
+
+      /* Free the server entry */
+      silc_idlist_del_data(user_data);
+      silc_idlist_del_server(server->local_list, user_data);
+      server->stat.my_servers--;
+      server->stat.servers--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_servers--;
+      break;
     }
-    break;
   default:
-    if (sock->user_data) {
-      /* We don't know what type of connection this is thus it must
-        be in authentication phase. */
-      cipher = ((SilcUnknownEntry)sock->user_data)->send_key;
-      hmac = ((SilcUnknownEntry)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcUnknownEntry)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcUnknownEntry)sock->user_data)->hmac_key_len;
-      }
+    {
+      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
+
+      silc_idlist_del_data(user_data);
+      silc_free(user_data);
+      break;
     }
-    break;
   }
 
-  if (dst_id) {
-    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
-    dst_id_len = silc_id_get_len(dst_id_type);
-  }
+  sock->user_data = NULL;
+}
 
-  /* Set the packet context pointers */
-  packetdata.type = type;
-  packetdata.flags = flags;
-  packetdata.src_id = silc_id_id2str(server->id, server->id_type);
-  packetdata.src_id_len = SILC_ID_SERVER_LEN;
-  packetdata.src_id_type = server->id_type;
-  packetdata.dst_id = dst_id_data;
-  packetdata.dst_id_len = dst_id_len;
-  packetdata.dst_id_type = dst_id_type;
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-  packetdata.rng = server->rng;
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_server_packet_send_prepare(server, sock, 
-                                 SILC_PACKET_HEADER_LEN +
-                                 packetdata.src_id_len + 
-                                 packetdata.dst_id_len,
-                                 packetdata.padlen,
-                                 data_len);
-
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Compute MAC of the packet */
-  if (hmac) {
-    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                           hmac_key, hmac_key_len, mac);
-    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
+/* Checks whether given channel has global users.  If it does this returns
+   TRUE and FALSE if there is only locally connected clients on the channel. */
+
+int silc_server_channel_has_global(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
+
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (chl->client->router)
+      return TRUE;
   }
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
+  return FALSE;
+}
 
-  /* Pull MAC into the visible data area */
-  if (hmac)
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
+/* Checks whether given channel has locally connected users.  If it does this
+   returns TRUE and FALSE if there is not one locally connected client. */
 
-  SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+int silc_server_channel_has_local(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
 
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (!chl->client->router)
+      return TRUE;
+  }
 
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+  return FALSE;
 }
 
-/* Forwards packet. Packets sent with this function will be marked as
-   forwarded (in the SILC header flags) so that the receiver knows that
-   we have forwarded the packet to it. Forwarded packets are handled
-   specially by the receiver as they are not destined to the receiver
-   originally. However, the receiver knows this because the forwarded
-   flag has been set (and the flag is authenticated). */
-
-void silc_server_packet_forward(SilcServer server,
-                               SilcSocketConnection sock,
-                               unsigned char *data, unsigned int data_len,
-                               int force_send)
+/* Removes client from all channels it has joined. This is used when client
+   connection is disconnected. If the client on a channel is last, the
+   channel is removed as well. This sends the SIGNOFF notify types. */
+
+void silc_server_remove_from_channels(SilcServer server, 
+                                     SilcSocketConnection sock,
+                                     SilcClientEntry client)
 {
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer chidp, clidp;
 
-  SILC_LOG_DEBUG(("Forwarding packet"));
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      cipher = ((SilcClientEntry )sock->user_data)->send_key;
-      hmac = ((SilcClientEntry )sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcClientEntry )sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcClientEntry )sock->user_data)->hmac_key_len;
-      }
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      cipher = ((SilcServerEntry )sock->user_data)->send_key;
-      hmac = ((SilcServerEntry )sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcServerEntry )sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcServerEntry )sock->user_data)->hmac_key_len;
-      }
-    }
-    break;
-  default:
-    /* We won't forward to unknown destination - keys must exist with
-       the destination before forwarding. */
+  if (!client || !client->id)
     return;
-  }
 
-  /* Prepare outgoing data buffer for packet sending */
-  silc_server_packet_send_prepare(server, sock, 0, 0, data_len);
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-  /* Mungle the packet flags and add the FORWARDED flag */
-  if (data)
-    data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED;
+  /* 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) {
+    channel = chl->channel;
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+
+    /* Remove from list */
+    silc_list_del(client->channels, chl);
+
+    /* If this client is last one on the channel the channel
+       is removed all together. */
+    if (silc_list_count(channel->user_list) < 2) {
+
+      /* However, if the channel has marked global users then the 
+        channel is not created locally, and this does not remove the
+        channel globally from SILC network, in this case we will
+        notify that this client has left the channel. */
+      if (channel->global_users)
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                          SILC_NOTIFY_TYPE_SIGNOFF, 1,
+                                          clidp->data, clidp->len);
+      
+      silc_idlist_del_channel(server->local_list, channel);
+      server->stat.my_channels--;
+      continue;
+    }
 
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+    /* Remove from list */
+    silc_list_del(channel->user_list, chl);
+    silc_free(chl);
+    server->stat.my_chanclients--;
 
-  /* Compute MAC of the packet */
-  if (hmac) {
-    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                           hmac_key, hmac_key_len, mac);
-    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
+    /* Send notify to channel about client leaving SILC and thus
+       the entire channel. */
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_SIGNOFF, 1,
+                                      clidp->data, clidp->len);
+    silc_buffer_free(chidp);
   }
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
-
-  /* Pull MAC into the visible data area */
-  if (hmac)
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
-
-  SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
+  silc_buffer_free(clidp);
 }
 
-/* This routine is used by the server to send packets to channel. The 
-   packet sent with this function is distributed to all clients on
-   the channel. Usually this is used to send notify messages to the
-   channel, things like notify about new user joining to the channel. */
+/* Removes client from one channel. This is used for example when client
+   calls LEAVE command to remove itself from the channel. Returns TRUE
+   if channel still exists and FALSE if the channel is removed when
+   last client leaves the channel. If `notify' is FALSE notify messages
+   are not sent. */
 
-void silc_server_packet_send_to_channel(SilcServer server,
+int silc_server_remove_from_one_channel(SilcServer server, 
+                                       SilcSocketConnection sock,
                                        SilcChannelEntry channel,
-                                       unsigned char *data,
-                                       unsigned int data_len,
-                                       int force_send)
+                                       SilcClientEntry client,
+                                       int notify)
 {
-  int i;
-  SilcSocketConnection sock = NULL;
-  SilcPacketContext packetdata;
-  SilcClientEntry client = NULL;
-  SilcServerEntry *routed = NULL;
-  unsigned int routed_count = 0;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  SilcCipher cipher;
-  SilcHmac hmac;
-  SilcBuffer payload;
-
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
-  /* Generate IV */
-  for (i = 0; i < 16; i++)
-    channel->iv[i] = silc_rng_get_byte(server->rng);
-
-  /* Encode the channel payload */
-  payload = silc_channel_encode_payload(0, "", data_len, data, 
-                                       16, channel->iv, server->rng);
-  if (!payload)
-    return;
-  
-  /* Encrypt payload of the packet. This is encrypted with the 
-     channel key. */
-  channel->channel_key->cipher->encrypt(channel->channel_key->context,
-                                       payload->data, payload->data,
-                                       payload->len - 16, /* -IV_LEN */
-                                       channel->iv);
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
-  packetdata.src_id_len = SILC_ID_SERVER_LEN;
-  packetdata.src_id_type = SILC_ID_SERVER;
-  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* If there are global users in the channel we will send the message
-     first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
-      channel->global_users) {
-    SilcServerEntry router;
-
-    /* Get data used in packet header encryption, keys and stuff. */
-    router = server->id_entry->router;
-    sock = (SilcSocketConnection)router->connection;
-    cipher = router->send_key;
-    hmac = router->hmac;
-    mac_len = hmac->hash->hash->hash_len;
-    hmac_key = router->hmac_key;
-    hmac_key_len = router->hmac_key_len;
-    
-    SILC_LOG_DEBUG(("Sending packet to router for routing"));
-
-    packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-      packetdata.src_id_len + packetdata.dst_id_len;
-
-    /* Prepare outgoing data buffer for packet sending */
-    silc_server_packet_send_prepare(server, sock, 
-                                   SILC_PACKET_HEADER_LEN +
-                                   packetdata.src_id_len + 
-                                   packetdata.dst_id_len,
-                                   packetdata.padlen,
-                                   payload->len);
-    packetdata.buffer = sock->outbuf;
-
-    /* Put the original packet into the buffer. */
-    silc_buffer_put(sock->outbuf, payload->data, payload->len);
-    
-    /* Create the outgoing packet */
-    silc_packet_assemble(&packetdata);
-    
-    /* Compute MAC of the packet. MAC is computed from the header,
-       padding and the relayed packet. */
-    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                           hmac_key, hmac_key_len, mac);
-    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-
-    /* Encrypt the header and padding of the packet. This is encrypted 
-       with normal session key shared with the client. */
-    silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                       packetdata.src_id_len + packetdata.dst_id_len +
-                       packetdata.padlen);
-    
-    /* Pull MAC into the visible data area */
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
-    
-    SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
-                    sock->outbuf->data, sock->outbuf->len);
-
-    /* Now actually send the packet */
-    silc_server_packet_send_real(server, sock, force_send);
-  }
-
-  /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
-
-    /* If client has router set it is not locally connected client and
-       we will route the message to the router set in the client. */
-    if (client && client->router && server->server_type == SILC_ROUTER) {
-      int k;
-
-      /* Check if we have sent the packet to this route already */
-      for (k = 0; k < routed_count; k++)
-       if (routed[k] == client->router)
-         continue;
-
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->router->connection;
-      cipher = client->router->send_key;
-      hmac = client->router->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = client->router->hmac_key;
-      hmac_key_len = client->router->hmac_key_len;
-      
-      packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     payload->len);
-      packetdata.buffer = sock->outbuf;
-
-      /* Put the encrypted payload data into the buffer. */
-      silc_buffer_put(sock->outbuf, payload->data, payload->len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
-      
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
-      
-      SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
+  SilcChannelEntry ch;
+  SilcChannelClientEntry chl;
+  SilcBuffer clidp;
 
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
+  SILC_LOG_DEBUG(("Start"));
 
-      /* We want to make sure that the packet is routed to same router
-        only once. Mark this route as sent route. */
-      k = routed_count;
-      routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
-      routed[k] = client->router;
-      routed_count++;
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
+  /* 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;
-    }
-
-    /* Send to locally connected client */
-    if (client) {
-
-      /* XXX Check client's mode on the channel. */
-
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->connection;
-      cipher = client->send_key;
-      hmac = client->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = client->hmac_key;
-      hmac_key_len = client->hmac_key_len;
-      
-      packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     payload->len);
-      packetdata.buffer = sock->outbuf;
-
-      /* Put the encrypted payload data into the buffer. */
-      silc_buffer_put(sock->outbuf, payload->data, payload->len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
-      
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
-      
-      SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
-
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
-    }
-  }
-
-  if (routed_count)
-    silc_free(routed);
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
-  silc_buffer_free(payload);
-}
-
-/* This routine is explicitly used to relay messages to some channel.
-   Packets sent with this function we have received earlier and are
-   totally encrypted. This just sends the packet to all clients on
-   the channel. If the sender of the packet is someone on the channel 
-   the message will not be sent to that client. The SILC Packet header
-   is encrypted with the session key shared between us and the client.
-   MAC is also computed before encrypting the header. Rest of the
-   packet will be untouched. */
-
-void silc_server_packet_relay_to_channel(SilcServer server,
-                                        SilcSocketConnection sender_sock,
-                                        SilcChannelEntry channel,
-                                        void *sender, 
-                                        SilcIdType sender_type,
-                                        unsigned char *data,
-                                        unsigned int data_len,
-                                        int force_send)
-{
-  int i, found = FALSE;
-  SilcSocketConnection sock = NULL;
-  SilcPacketContext packetdata;
-  SilcClientEntry client = NULL;
-  SilcServerEntry *routed = NULL;
-  unsigned int routed_count = 0;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  SILC_LOG_DEBUG(("Relaying packet to channel"));
-
-  SILC_LOG_HEXDUMP(("XXX %d", data_len), data, data_len);
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = silc_id_id2str(sender, sender_type);
-  packetdata.src_id_len = silc_id_get_len(sender_type);
-  packetdata.src_id_type = sender_type;
-  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* If there are global users in the channel we will send the message
-     first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
-      channel->global_users) {
-    SilcServerEntry router;
-
-    router = server->id_entry->router;
-
-    /* Check that the sender is not our router. */
-    if (sender_sock != (SilcSocketConnection)router->connection) {
-
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)router->connection;
-      cipher = router->send_key;
-      hmac = router->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = router->hmac_key;
-      hmac_key_len = router->hmac_key_len;
-      
-      SILC_LOG_DEBUG(("Sending packet to router for routing"));
-      
-      packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-      
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     data_len);
-      packetdata.buffer = sock->outbuf;
-      
-      /* Put the original packet into the buffer. */
-      silc_buffer_put(sock->outbuf, data, data_len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-      
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
-      
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
-      
-      SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
-      
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
-    }
-  }
-
-  /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
-
-    if (client) {
 
-      /* If sender is one on the channel do not send it the packet. */
-      if (!found && !SILC_ID_CLIENT_COMPARE(client->id, sender)) {
-       found = TRUE;
-       continue;
-      }
-
-      /* If the client has set router it means that it is not locally
-        connected client and we will route the packet further. */
-      if (server->server_type == SILC_ROUTER && client->router) {
-       int k;
+    ch = chl->channel;
 
-       /* Sender maybe server as well so we want to make sure that
-          we won't send the message to the server it came from. */
-       if (!found && !SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
-         found = TRUE;
-         continue;
-       }
+    /* Remove from list */
+    silc_list_del(client->channels, chl);
 
-       /* Check if we have sent the packet to this route already */
-       for (k = 0; k < routed_count; k++)
-         if (routed[k] == client->router)
-           continue;
-       
-       /* Get data used in packet header encryption, keys and stuff. */
-       sock = (SilcSocketConnection)client->router->connection;
-       cipher = client->router->send_key;
-       hmac = client->router->hmac;
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = client->router->hmac_key;
-       hmac_key_len = client->router->hmac_key_len;
-       
-       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-         packetdata.src_id_len + packetdata.dst_id_len;
-       
-       /* Prepare outgoing data buffer for packet sending */
-       silc_server_packet_send_prepare(server, sock, 
-                                       SILC_PACKET_HEADER_LEN +
-                                       packetdata.src_id_len + 
-                                       packetdata.dst_id_len,
-                                       packetdata.padlen,
-                                       data_len);
-       packetdata.buffer = sock->outbuf;
-       
-       /* Put the original packet into the buffer. */
-       silc_buffer_put(sock->outbuf, data, data_len);
-       
-       /* Create the outgoing packet */
-       silc_packet_assemble(&packetdata);
-       
-       /* Compute MAC of the packet. MAC is computed from the header,
-          padding and the relayed packet. */
-       silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                               hmac_key, hmac_key_len, mac);
-       silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-       memset(mac, 0, sizeof(mac));
-       
-       /* Encrypt the header and padding of the packet. This is encrypted 
-          with normal session key shared with the client. */
-       silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                           packetdata.src_id_len + packetdata.dst_id_len +
-                           packetdata.padlen);
-       
-       /* Pull MAC into the visible data area */
-       silc_buffer_pull_tail(sock->outbuf, mac_len);
-       
-       SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                        sock->outbuf->data, sock->outbuf->len);
-       
-       /* Now actually send the packet */
-       silc_server_packet_send_real(server, sock, force_send);
-       
-       /* We want to make sure that the packet is routed to same router
-          only once. Mark this route as sent route. */
-       k = routed_count;
-       routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
-       routed[k] = client->router;
-       routed_count++;
-       
-       continue;
-      }
+    /* If this client is last one on the channel the channel
+       is removed all together. */
+    if (silc_list_count(channel->user_list) < 2) {
+      /* Notify about leaving client if this channel has global users. */
+      if (notify && channel->global_users)
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                          SILC_NOTIFY_TYPE_LEAVE, 1,
+                                          clidp->data, clidp->len);
       
-      /* XXX Check client's mode on the channel. */
-
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->connection;
-      cipher = client->send_key;
-      hmac = client->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = client->hmac_key;
-      hmac_key_len = client->hmac_key_len;
-      
-      SILC_LOG_DEBUG(("Sending packet to client %s", 
-                     sock->hostname ? sock->hostname : sock->ip));
-
-      packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     data_len);
-      packetdata.buffer = sock->outbuf;
-
-      /* Put the original packet into the buffer. */
-      silc_buffer_put(sock->outbuf, data, data_len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
-      
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
-      
-      SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
-
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
-    }
-  }
-
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
-}
-
-/* This function is used to send packets strictly to all local clients
-   on a particular channel.  This is used for example to distribute new
-   channel key to all our locally connected clients on the channel. 
-   The packets are always encrypted with the session key shared between
-   the client. */
-
-void silc_server_packet_send_local_channel(SilcServer server,
-                                          SilcChannelEntry channel,
-                                          SilcPacketType type,
-                                          SilcPacketFlags flags,
-                                          unsigned char *data,
-                                          unsigned int data_len,
-                                          int force_send)
-{
-  int i;
-  SilcClientEntry client;
-  SilcSocketConnection sock = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
-
-    if (client) {
-      sock = (SilcSocketConnection)client->connection;
-
-      /* Send the packet to the client */
-      silc_server_packet_send_dest(server, sock, type, flags, client->id,
-                                  SILC_ID_CLIENT, data, data_len,
-                                  force_send);
+      silc_idlist_del_channel(server->local_list, channel);
+      silc_buffer_free(clidp);
+      server->stat.my_channels--;
+      return FALSE;
     }
-  }
-}
-
-/* Relays received command reply packet to the correct destination. The
-   destination must be one of our locally connected client or the packet
-   will be ignored. This is called when server has forwarded one of
-   client's command request to router and router has now replied to the 
-   command. */
-
-void silc_server_packet_relay_command_reply(SilcServer server,
-                                           SilcSocketConnection sock,
-                                           SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client;
-  SilcClientID *id;
-  SilcSocketConnection dst_sock;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Source must be server or router */
-  /* XXX: actually it must be only router */
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      (sock->type != SILC_SOCKET_TYPE_SERVER ||
-       sock->type != SILC_SOCKET_TYPE_ROUTER))
-    goto out;
-
-  /* Destination must be client */
-  if (packet->dst_id_type != SILC_ID_CLIENT)
-    goto out;
-
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
-
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
-
-  /* Destination must be one of ours */
-  client = silc_idlist_find_client_by_id(server->local_list, id);
-  if (!client) {
-    silc_free(id);
-    goto out;
-  }
-
-  /* Relay the packet to the client */
-  if (client->hmac)
-    mac_len = client->hmac->hash->hash->hash_len;
-
-  dst_sock = (SilcSocketConnection)client->connection;
-
-  silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                  + packet->dst_id_len + packet->padlen);
-  silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-  silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-  
-  /* Compute new HMAC */
-  if (client->hmac) {
-    memset(mac, 0, sizeof(mac));
-    silc_hmac_make_with_key(client->hmac, 
-                           dst_sock->outbuf->data, 
-                           dst_sock->outbuf->len,
-                           client->hmac_key, 
-                           client->hmac_key_len, 
-                           mac);
-    silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-  }
-    
-  /* Encrypt */
-  if (client && client->send_key)
-    silc_packet_encrypt(client->send_key, dst_sock->outbuf, buffer->len);
-    
-  if (client->hmac)
-    silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
-    
-  /* Send the packet */
-  silc_server_packet_send_real(server, dst_sock, FALSE);
-
-  silc_free(id);
-
- out:
-  silc_buffer_free(buffer);
-}
-
-/* Closes connection to socket connection */
-
-void silc_server_close_connection(SilcServer server,
-                                 SilcSocketConnection sock)
-{
-
-  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
-
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
-
-  /* Unregister all tasks */
-  silc_task_unregister_by_fd(server->io_queue, sock->sock);
-  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
-
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
-  server->sockets[sock->sock] = NULL;
-  silc_socket_free(sock);
-}
-
-/* Sends disconnect message to remote connection and disconnects the 
-   connection. */
-
-void silc_server_disconnect_remote(SilcServer server,
-                                  SilcSocketConnection sock,
-                                  const char *fmt, ...)
-{
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  SILC_LOG_DEBUG(("Disconnecting remote host"));
-
-  /* Notify remote end that the conversation is over. The notify message
-     is tried to be sent immediately. */
-  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
-                         buf, strlen(buf), TRUE);
-
-  /* Mark the connection to be disconnected */
-  SILC_SET_DISCONNECTED(sock);
-  silc_server_close_connection(server, sock);
-}
-
-/* Free's user_data pointer from socket connection object. As this 
-   pointer maybe anything we wil switch here to find the corrent
-   data type and free it the way it needs to be free'd. */
-
-void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock)
-{
-  SILC_LOG_DEBUG(("Start"));
-
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    {
-      SilcClientEntry user_data = (SilcClientEntry )sock->user_data;
-
-      /* Remove client from all channels */
-      silc_server_remove_from_channels(server, sock, user_data);
 
-      /* Free the client entry and everything in it */
-      /* XXX must take some info to history before freeing */
-      silc_idlist_del_client(server->local_list, user_data);
-      break;
+    /* Remove from 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 tehre 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)) {
+      silc_idlist_del_channel(server->local_list, channel);
+      silc_buffer_free(clidp);
+      server->stat.my_channels--;
+      return FALSE;
     }
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    {
 
-      break;
-    }
+    /* 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;
-  default:
-    {
-      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
-
-      if (user_data->send_key)
-       silc_cipher_free(user_data->send_key);
-      if (user_data->receive_key)
-       silc_cipher_free(user_data->receive_key);
-      if (user_data->pkcs)
-       silc_pkcs_free(user_data->pkcs);
-      if (user_data->hmac) {
-       silc_hmac_free(user_data->hmac);
-       memset(user_data->hmac_key, 0, user_data->hmac_key_len);
-       silc_free(user_data->hmac_key);
-      }
-      silc_free(user_data);
-      break;
-    }
-  }
-
-  sock->user_data = NULL;
-#undef LCC
-#undef LCCC
-}
-
-/* Removes client from all channels it has joined. This is used when
-   client connection is disconnected. If the client on a channel
-   is last, the channel is removed as well. */
-
-void silc_server_remove_from_channels(SilcServer server, 
-                                     SilcSocketConnection sock,
-                                     SilcClientEntry client)
-{
-  int i, k;
-  SilcChannelEntry channel;
-
-  /* Remove the client from all channels. The client is removed from
-     the channels' user list. */
-  for (i = 0; i < client->channel_count; i++) {
-    channel = client->channel[i];
-    if (!channel)
-      continue;
-
-    /* Remove from channel */
-    for (k = 0; k < channel->user_list_count; k++) {
-      if (channel->user_list[k].client == client) {
-
-       /* If this client is last one on the channel the channel
-          is removed all together. */
-       if (channel->user_list_count == 1) {
-         silc_idlist_del_channel(server->local_list, channel);
-         break;
-       }
-
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
-
-       /* Send notify to channel about client leaving SILC and thus
-          the entire channel. */
-       silc_server_send_notify_to_channel(server, channel,
-                                          "Signoff: %s",
-                                          client->nickname);
-      }
-    }
-  }
-
-  if (client->channel_count)
-    silc_free(client->channel);
-  client->channel = NULL;
-}
-
-/* Removes client from one channel. This is used for example when client
-   calls LEAVE command to remove itself from the channel. Returns TRUE
-   if channel still exists and FALSE if the channel is removed when
-   last client leaves the channel. */
-
-int silc_server_remove_from_one_channel(SilcServer server, 
-                                       SilcSocketConnection sock,
-                                       SilcChannelEntry channel,
-                                       SilcClientEntry client)
-{
-  int i, k;
-  SilcChannelEntry ch;
-
-  /* Remove the client from the channel. The client is removed from
-     the channel's user list. */
-  for (i = 0; i < client->channel_count; i++) {
-    ch = client->channel[i];
-    if (!ch || ch != channel)
-      continue;
-
-    /* XXX */
-    client->channel[i] = NULL;
-
-    /* Remove from channel */
-    for (k = 0; k < channel->user_list_count; k++) {
-      if (channel->user_list[k].client == client) {
-       
-       /* If this client is last one on the channel the channel
-          is removed all together. */
-       if (channel->user_list_count == 1) {
-         silc_idlist_del_channel(server->local_list, channel);
-         return FALSE;
-       }
-       
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
-
-       /* Send notify to channel about client leaving the channel */
-       silc_server_send_notify_to_channel(server, channel,
-                                          "%s has left channel %s",
-                                          client->nickname,
-                                          channel->channel_name);
-      }
-    }
   }
 
+  silc_buffer_free(clidp);
   return TRUE;
 }
 
@@ -3033,20 +1918,19 @@ int silc_server_remove_from_one_channel(SilcServer server,
    This works because we assure that the user list on the channel is
    always in up to date thus we can only check the channel list from 
    `client' which is faster than checking the user list from `channel'. */
-/* XXX This really is utility function and should be in eg. serverutil.c */
 
 int silc_server_client_on_channel(SilcClientEntry client,
                                  SilcChannelEntry channel)
 {
-  int i;
+  SilcChannelClientEntry chl;
 
   if (!client || !channel)
     return FALSE;
 
-  for (i = 0; i < client->channel_count; i++) {
-    if (client->channel[i] == channel)
+  silc_list_start(client->channels);
+  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END)
+    if (chl->channel == channel)
       return TRUE;
-  }
 
   return FALSE;
 }
@@ -3060,813 +1944,185 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = server->sockets[fd];
 
-  silc_server_disconnect_remote(server, sock, 
-                               "Server closed connection: "
-                               "Connection timeout");
-}
-
-/* Internal routine used to send (relay, route) private messages to some
-   destination. This is used to by normal server to send the message to
-   its primary route and router uses this to send it to any route it
-   wants. If the private message key does not exist then the message
-   is re-encrypted, otherwise we just pass it along. */
-static void 
-silc_server_private_message_send_internal(SilcServer server,
-                                         SilcSocketConnection dst_sock,
-                                         SilcServerEntry router,
-                                         SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-
-  /* Send and re-encrypt if private messge key does not exist */
-  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
-    unsigned char mac[32];
-    unsigned int mac_len = 0;
-    
-    if (router->hmac)
-      mac_len = router->hmac->hash->hash->hash_len;
-    
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    
-    /* Compute new HMAC */
-    if (router->hmac) {
-      mac_len = router->hmac->hash->hash->hash_len;
-      memset(mac, 0, sizeof(mac));
-      silc_hmac_make_with_key(router->hmac, 
-                             dst_sock->outbuf->data, 
-                             dst_sock->outbuf->len,
-                             router->hmac_key, 
-                             router->hmac_key_len, 
-                             mac);
-      silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-    }
-    
-    silc_packet_encrypt(router->send_key, dst_sock->outbuf, buffer->len);
-    
-    if (router->hmac)
-      silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
-    
-    /* Send the packet */
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-
-  } else {
-    /* Key exist so just send it */
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-  }
-}
-
-/* Internal routine to send the received private message packet to
-   our locally connected client. */
-static void
-silc_server_private_message_send_local(SilcServer server,
-                                      SilcSocketConnection dst_sock,
-                                      SilcClientEntry client,
-                                      SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-
-  /* Re-encrypt packet if needed */
-  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
-    unsigned char mac[32];
-    unsigned int mac_len = 0;
-
-    if (client->hmac)
-      mac_len = client->hmac->hash->hash->hash_len;
-    
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    
-    /* Compute new HMAC */
-    if (client->hmac) {
-      memset(mac, 0, sizeof(mac));
-      silc_hmac_make_with_key(client->hmac, 
-                             dst_sock->outbuf->data, 
-                             dst_sock->outbuf->len,
-                             client->hmac_key, 
-                             client->hmac_key_len, 
-                             mac);
-      silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-    }
-    
-    /* Encrypt */
-    if (client && client->send_key)
-      silc_packet_encrypt(client->send_key, dst_sock->outbuf, 
-                         buffer->len);
-    
-    if (client->hmac)
-      silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
-    
-    /* Send the packet */
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-  } else {
-    /* Key exist so just send it */
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-  }
-}
-
-/* Received private message. This resolves the destination of the message 
-   and sends the packet. This is used by both server and router.  If the
-   destination is our locally connected client this sends the packet to
-   the client. This may also send the message for further routing if
-   the destination is not in our server (or router). */
-
-void silc_server_private_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcClientID *id;
-  SilcServerEntry router;
-  SilcSocketConnection dst_sock;
-  SilcClientEntry client;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!packet->dst_id) {
-    SILC_LOG_DEBUG(("Bad Client ID in private message packet"));
-    goto err;
-  }
-
-  /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
-  if (!id) {
-    SILC_LOG_DEBUG(("Could not decode destination Client ID"));
-    goto err;
-  }
-
-  /* If the destination belongs to our server we don't have to route
-     the message anywhere but to send it to the local destination. */
-  /* XXX: Should use local cache to search but the current idcache system
-     is so sucky that it cannot be used... it MUST be rewritten! Using
-     this search is probably faster than if we'd use here the current
-     idcache system. */
-  client = silc_idlist_find_client_by_id(server->local_list, id);
-  if (client) {
-    /* It exists, now deliver the message to the destination */
-    dst_sock = (SilcSocketConnection)client->connection;
-
-    /* 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) {
-      silc_server_private_message_send_internal(server, dst_sock,
-                                               client->router, packet);
-      goto out;
-    }
-
-    /* Seems that client really is directly connected to us */
-    silc_server_private_message_send_local(server, dst_sock, client, packet);
-    goto out;
-  }
-
-  /* Destination belongs to someone not in this server. If we are normal
-     server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
-    router = server->id_entry->router;
-    dst_sock = (SilcSocketConnection)router->connection;
-
-    /* Send to primary route */
-    silc_server_private_message_send_internal(server, dst_sock, router,
-                                             packet);
-    goto out;
-  }
-
-  /* We are router and we will perform route lookup for the destination 
-     and send the message to fastest route. */
-  if (server->server_type == SILC_ROUTER && !server->standalone) {
-
-    /* Get fastest route and send packet. */
-    dst_sock = silc_server_get_route(server, id, SILC_ID_CLIENT);
-    silc_server_private_message_send_internal(server, dst_sock, 
-                                             dst_sock->user_data, packet);
-    goto out;
-  }
-
- err:
-  silc_server_send_error(server, sock, 
-                        "No such nickname: Private message not sent");
- out:
-  silc_buffer_free(buffer);
-}
-
-/* Process received channel message. */
-
-void silc_server_channel_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
-{
-  SilcChannelEntry channel = NULL;
-  SilcClientEntry client = NULL;
-  SilcChannelID *id = NULL;
-  SilcClientID *sender = NULL;
-  SilcBuffer buffer = packet->buffer;
-  int i;
-
-  SILC_LOG_DEBUG(("Processing channel message"));
-  
-  /* Check MAC */
-  if (!silc_server_packet_check_mac(server, sock, buffer))
-    goto out;
-
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Received bad message for channel, dropped"));
-    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
-    goto out;
-  }
-
-  /* Find channel entry */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
-  if (!channel) {
-    SILC_LOG_DEBUG(("Could not find channel"));
-    goto out;
-  }
-
-  /* See that this client is on the channel */
-  sender = silc_id_str2id(packet->src_id, packet->src_id_type);
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
-    if (client && !SILC_ID_CLIENT_COMPARE(client->id, sender))
-      break;
-  }
-  if (i >= channel->user_list_count)
-    goto out;
-
-  /* Distribute the packet to our local clients. This will send the
-     packet for further routing as well, if needed. */
-  silc_server_packet_relay_to_channel(server, sock, channel, sender,
-                                     packet->src_id_type,
-                                     packet->buffer->data,
-                                     packet->buffer->len, FALSE);
-
- out:
-  if (sender)
-    silc_free(sender);
-  if (id)
-    silc_free(id);
-  silc_buffer_free(buffer);
-}
-
-/* Received channel key packet. We distribute the key to all of our locally
-   connected clients on the channel. */
-/* XXX Router must accept this packet and distribute the key to all its
-   server that has clients on the channel */
-
-void silc_server_channel_key(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcChannelKeyPayload payload = NULL;
-  SilcChannelID *id = NULL;
-  SilcChannelEntry channel;
-  SilcClientEntry client;
-  unsigned char *key;
-  unsigned int key_len;
-  char *cipher;
-  int i;
-
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    goto out;
-
-  /* Decode channel key payload */
-  payload = silc_channel_key_parse_payload(buffer);
-  if (!payload) {
-    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
-    goto out;
-  }
-
-  /* Get channel ID */
-  id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL);
-  if (!id)
-    goto out;
-
-  /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
-  if (!channel) {
-    SILC_LOG_ERROR(("Received key for non-existent channel"));
-    goto out;
-  }
-
-  /* Save the key for us as well */
-  key = silc_channel_key_get_key(payload, &key_len);
-  if (!key)
-    goto out;
-  cipher = silc_channel_key_get_cipher(payload, NULL);;
-  if (!cipher)
-    goto out;
-  channel->key_len = key_len * 8;
-  channel->key = silc_calloc(key_len, sizeof(unsigned char));
-  memcpy(channel->key, key, key_len);
-  silc_cipher_alloc(cipher, &channel->channel_key);
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       key, key_len);
-
-  /* Distribute the key to all clients on the channel */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
-
-    if (client)
-      silc_server_packet_send_dest(server, client->connection,
-                                  SILC_PACKET_CHANNEL_KEY, 0,
-                                  client->id, SILC_ID_CLIENT,
-                                  buffer->data, buffer->len, FALSE);
-  }
-
- out:
-  if (id)
-    silc_free(id);
-  if (payload)
-    silc_channel_key_free_payload(payload);
-  silc_buffer_free(buffer);
-}
-
-/* Sends error message. Error messages may or may not have any 
-   implications. */
-
-void silc_server_send_error(SilcServer server,
-                           SilcSocketConnection sock,
-                           const char *fmt, ...)
-{
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
-                         buf, strlen(buf), FALSE);
-}
-
-/* Sends notify message */
-
-void silc_server_send_notify(SilcServer server,
-                            SilcSocketConnection sock,
-                            const char *fmt, ...)
-{
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, 
-                         buf, strlen(buf), FALSE);
-}
-
-/* Sends notify message destined to specific entity. */
-
-void silc_server_send_notify_dest(SilcServer server,
-                                 SilcSocketConnection sock,
-                                 void *dest_id,
-                                 SilcIdType dest_id_type,
-                                 const char *fmt, ...)
-{
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 0, 
-                              dest_id, dest_id_type,
-                              buf, strlen(buf), FALSE);
-}
-
-/* Sends notify message to a channel. The notify message sent is 
-   distributed to all clients on the channel. Actually this is not real
-   notify message, instead it is message to channel sent by server. But
-   as server is sending it it will appear as notify type message on the
-   client side. */
-
-void silc_server_send_notify_to_channel(SilcServer server,
-                                       SilcChannelEntry channel,
-                                       const char *fmt, ...)
-{
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  silc_server_packet_send_to_channel(server, channel, buf, 
-                                    strlen(buf), FALSE);
-}
-
-/* Sends New ID Payload to remote end. The packet is used to distribute
-   information about new registered clients, servers, channel etc. usually
-   to routers so that they can keep these information up to date. 
-   If the argument `broadcast' is TRUE then the packet is sent as
-   broadcast packet. */
-
-void silc_server_send_new_id(SilcServer server,
-                            SilcSocketConnection sock,
-                            int broadcast,
-                            void *id, SilcIdType id_type, 
-                            unsigned int id_len)
-{
-  SilcBuffer packet;
-  unsigned char *id_string;
-
-  id_string = silc_id_id2str(id, id_type);
-  if (!id_string)
-    return;
-
-  packet = silc_buffer_alloc(2 + 2 + id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(id_type),
-                    SILC_STR_UI_SHORT(id_len),
-                    SILC_STR_UI_XNSTRING(id_string, id_len),
-                    SILC_STR_END);
-
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(packet);
-}
-
-/* Sends Replace ID payload to remote end. This is used to replace old
-   ID with new ID sent in the packet.  This is called for example when
-   user changes nickname and we create new ID for the user.  If the 
-   argument `broadcast' is TRUE then the packet is sent as
-   broadcast packet. */
-/* XXX It would be expected that the new id is same type as the old
-   ID. :) */
-
-void silc_server_send_replace_id(SilcServer server,
-                                SilcSocketConnection sock,
-                                int broadcast,
-                                void *old_id, SilcIdType old_id_type,
-                                unsigned int old_id_len,
-                                void *new_id, SilcIdType new_id_type,
-                                unsigned int new_id_len)
-{
-  SilcBuffer packet;
-  unsigned char *oid;
-  unsigned char *nid;
-
-  oid = silc_id_id2str(old_id, old_id_type);
-  if (!oid)
-    return;
-
-  nid = silc_id_id2str(new_id, new_id_type);
-  if (!nid)
+  if (!sock)
     return;
 
-  packet = silc_buffer_alloc(2 + 2 + 2 + 2 + old_id_len + new_id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(old_id_type),
-                    SILC_STR_UI_SHORT(old_id_len),
-                    SILC_STR_UI_XNSTRING(oid, old_id_len),
-                    SILC_STR_UI_SHORT(new_id_type),
-                    SILC_STR_UI_SHORT(new_id_len),
-                    SILC_STR_UI_XNSTRING(nid, new_id_len),
-                    SILC_STR_END);
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock);
 
-  silc_server_packet_send(server, sock, SILC_PACKET_REPLACE_ID, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(oid);
-  silc_free(nid);
-  silc_buffer_free(packet);
+  silc_server_disconnect_remote(server, sock, 
+                               "Server closed connection: "
+                               "Connection timeout");
 }
 
-/* Creates new channel. */
+/* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
+   function may be used only by router. In real SILC network all channels
+   are created by routers thus this function is never used by normal
+   server. */
 
-SilcChannelEntry silc_server_new_channel(SilcServer server, 
-                                        SilcServerID *router_id,
-                                        char *cipher, char *channel_name)
+SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
+                                               SilcServerID *router_id,
+                                               char *cipher, 
+                                               char *channel_name)
 {
-  int i, channel_len, key_len;
   SilcChannelID *channel_id;
   SilcChannelEntry entry;
   SilcCipher key;
-  unsigned char channel_key[32], *id_string;
-  SilcBuffer packet;
 
   SILC_LOG_DEBUG(("Creating new channel"));
 
-  /* Create channel key */
-  for (i = 0; i < 32; i++)
-    channel_key[i] = silc_rng_get_byte(server->rng);
-
   if (!cipher)
     cipher = "twofish";
 
-  /* Allocate keys */
-  key_len = 16;
+  /* Allocate cipher */
   silc_cipher_alloc(cipher, &key);
-  key->cipher->set_key(key->context, channel_key, key_len);
+
+  channel_name = strdup(channel_name);
 
   /* Create the channel */
   silc_id_create_channel_id(router_id, server->rng, &channel_id);
   entry = silc_idlist_add_channel(server->local_list, channel_name, 
                                  SILC_CHANNEL_MODE_NONE, channel_id, 
                                  NULL, key);
-  if (!entry)
+  if (!entry) {
+    silc_free(channel_name);
     return NULL;
+  }
 
-  /* Add to cache */
-  silc_idcache_add(server->local_list->channels, channel_name,
-                  SILC_ID_CHANNEL, channel_id, (void *)entry, TRUE);
-
-  entry->key = silc_calloc(key_len, sizeof(*entry->key));
-  entry->key_len = key_len * 8;
-  memcpy(entry->key, channel_key, key_len);
-  memset(channel_key, 0, sizeof(channel_key));
+  /* Now create the actual key material */
+  silc_server_create_channel_key(server, entry, 16);
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
   if (server->standalone == FALSE) {
-    channel_len = strlen(channel_name);
-    id_string = silc_id_id2str(entry->id, SILC_ID_CHANNEL);
-    packet = silc_buffer_alloc(2 + SILC_ID_CHANNEL_LEN);
-
-    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-    silc_buffer_format(packet,
-                      SILC_STR_UI_SHORT(channel_len),
-                      SILC_STR_UI_XNSTRING(channel_name, channel_len),
-                      SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                      SILC_STR_UI_XNSTRING(id_string, SILC_ID_CHANNEL_LEN),
-                      SILC_STR_END);
-
-    /* Send the packet to our router. */
-    silc_server_packet_send(server, (SilcSocketConnection) 
-                           server->id_entry->router->connection,
-                           SILC_PACKET_NEW_CHANNEL_USER, 0, 
-                           packet->data, packet->len, TRUE);
-    
-    silc_free(id_string);
-    silc_buffer_free(packet);
+    silc_server_send_new_channel(server, server->router->connection, TRUE, 
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
   }
 
+  server->stat.my_channels++;
+
   return entry;
 }
 
-/* Create new client. This processes incoming NEW_CLIENT packet and creates
-   Client ID for the client. Client becomes registered after calling this
-   functions. */
+/* Generates new channel key. This is used to create the initial channel key
+   but also to re-generate new key for channel. If `key_len' is provided
+   it is the bytes of the key length. */
 
-SilcClientEntry silc_server_new_client(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+void silc_server_create_channel_key(SilcServer server, 
+                                   SilcChannelEntry channel,
+                                   unsigned int key_len)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client;
-  SilcIDCacheEntry cache;
-  SilcClientID *client_id;
-  SilcBuffer reply;
-  char *username = NULL, *realname = NULL, *id_string;
-
-  SILC_LOG_DEBUG(("Creating new client"));
+  int i;
+  unsigned char channel_key[32];
+  unsigned int len;
 
-  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
-    return NULL;
+  if (!channel->channel_key)
+    silc_cipher_alloc("twofish", &channel->channel_key);
 
-  /* Take client entry */
-  client = (SilcClientEntry)sock->user_data;
+  if (key_len)
+    len = key_len;
+  else if (channel->key_len)
+    len = channel->key_len / 8;
+  else
+    len = sizeof(channel_key);
 
-  /* Fetch the old client cache entry so that we can update it. */
-  if (!silc_idcache_find_by_context(server->local_list->clients,
-                                   sock->user_data, &cache)) {
-    SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
-    return NULL;
-  }
-
-  /* Parse incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&username),
-                      SILC_STR_UI16_STRING_ALLOC(&realname),
-                      SILC_STR_END);
-
-  /* Create Client ID */
-  silc_id_create_client_id(server->id, server->rng, server->md5hash,
-                          username, &client_id);
-
-  /* Update client entry */
-  client->registered = TRUE;
-  client->nickname = strdup(username);
-  client->username = username;
-  client->userinfo = realname;
-  client->id = client_id;
-
-  /* Update the cache entry */
-  cache->id = (void *)client_id;
-  cache->type = SILC_ID_CLIENT;
-  cache->data = username;
-  silc_idcache_sort_by_data(server->local_list->clients);
-
-  /* Notify our router about new client on the SILC network */
-  if (!server->standalone)
-    silc_server_send_new_id(server, (SilcSocketConnection) 
-                           server->id_entry->router->connection, 
-                           server->server_type == SILC_SERVER ? TRUE : FALSE,
-                           client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
-  
-  /* Send the new client ID to the client. */
-  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
-  reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
-  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
-  silc_buffer_format(reply,
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT),
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
-                    SILC_STR_END);
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
-                         reply->data, reply->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(reply);
+  /* Create channel key */
+  for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
   
-  /* Send some nice info to the client */
-  silc_server_send_notify(server, sock, 
-                         "Welcome to the SILC Network %s@%s",
-                         username, 
-                         sock->hostname ? sock->hostname : sock->ip);
-  silc_server_send_notify(server, sock,
-                         "Your host is %s, running version %s",
-                         server->config->server_info->server_name,
-                         server_version);
-  silc_server_send_notify(server, sock, 
-                         "Your connection is secured with %s cipher, "
-                         "key length %d bits",
-                         client->send_key->cipher->name,
-                         client->send_key->cipher->key_len);
-  silc_server_send_notify(server, sock, 
-                         "Your current nickname is %s",
-                         client->nickname);
-
-  /* XXX Send motd */
-
-  return client;
-}
-
-/* Create new server. This processes incoming NEW_SERVER packet and
-   saves the received Server ID. The server is our locally connected
-   server thus we save all the information and save it to local list. 
-   This funtion can be used by both normal server and router server.
-   If normal server uses this it means that its router has connected
-   to the server. If router uses this it means that one of the cell's
-   servers is connected to the router. */
-
-SilcServerEntry silc_server_new_server(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcServerEntry new_server;
-  SilcIDCacheEntry cache;
-  SilcServerID *server_id;
-  unsigned char *server_name, *id_string;
-
-  SILC_LOG_DEBUG(("Creating new server"));
-
-  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return NULL;
-
-  /* Take server entry */
-  new_server = (SilcServerEntry)sock->user_data;
+  /* Set the key */
+  channel->channel_key->cipher->set_key(channel->channel_key->context, 
+                                       channel_key, len);
 
-  /* Fetch the old server cache entry so that we can update it. */
-  if (!silc_idcache_find_by_context(server->local_list->servers,
-                                   sock->user_data, &cache)) {
-    SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
-    return NULL;
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
   }
 
-  /* Parse the incoming packet */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_UI16_STRING_ALLOC(&server_name),
-                      SILC_STR_END);
-
-  /* Get Server ID */
-  server_id = silc_id_str2id(id_string, SILC_ID_SERVER);
-  silc_free(id_string);
-
-  /* Update client entry */
-  new_server->registered = TRUE;
-  new_server->server_name = server_name;
-  new_server->id = server_id;
-
-  /* Update the cache entry */
-  cache->id = (void *)server_id;
-  cache->type = SILC_ID_SERVER;
-  cache->data = server_name;
-  silc_idcache_sort_by_data(server->local_list->servers);
-
-  /* Distribute the information about new server in the SILC network
-     to our router. If we are normal server we won't send anything
-     since this connection must be our router connection. */
-  if (server->server_type == SILC_ROUTER && !server->standalone)
-    silc_server_send_new_id(server, server->id_entry->router->connection,
-                           TRUE, new_server->id, SILC_ID_SERVER, 
-                           SILC_ID_SERVER_LEN);
-
-  return new_server;
+  /* Save the key */
+  channel->key_len = len * 8;
+  channel->key = silc_calloc(len, sizeof(*channel->key));
+  memcpy(channel->key, channel_key, len);
+  memset(channel_key, 0, sizeof(channel_key));
 }
 
-/* Processes incoming New ID Payload. New ID Payload is used to distribute
-   information about newly registered clients, servers and created 
-   channels. */
+/* Saves the channel key found in the encoded `key_payload' buffer. This 
+   function is used when we receive Channel Key Payload and also when we're
+   processing JOIN command reply. Returns entry to the channel. */
 
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
-                       SilcPacketContext *packet)
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcIdType id_type;
-  unsigned char *id_string;
-  void *id;
-
-  SILC_LOG_DEBUG(("Processing new ID"));
-
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
-    return;
-
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&id_type),
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_END);
-
-  /* Normal server cannot have other normal server connections */
-  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
-    goto out;
+  SilcChannelKeyPayload payload = NULL;
+  SilcChannelID *id = NULL;
+  unsigned char *tmp;
+  unsigned int tmp_len;
+  char *cipher;
 
-  id = silc_id_str2id(id_string, id_type);
-  if (!id)
+  /* Decode channel key payload */
+  payload = silc_channel_key_payload_parse(key_payload);
+  if (!payload) {
+    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    channel = NULL;
     goto out;
+  }
 
-  /* XXX Do check whether the packet is coming outside the cell or
-     from someone inside the cell.  If outside use global lists otherwise
-     local lists. */
-  /* XXX If using local list set the idlist->connection to the sender's
-     socket connection as it is used in packet sending */
+  /* Get the channel entry */
+  if (!channel) {
 
-  switch(id_type) {
-  case SILC_ID_CLIENT:
-    {
-      SilcClientEntry idlist;
+    /* Get channel ID */
+    tmp = silc_channel_key_get_id(payload, &tmp_len);
+    id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
+    if (!id) {
+      channel = NULL;
+      goto out;
+    }
 
-      /* Add the client to our local list. We are router and we keep
-        cell specific local database of all clients in the cell. */
-      idlist = silc_idlist_add_client(server->local_list, NULL, NULL, NULL,
-                                     id, sock->user_data, NULL, NULL, 
-                                     NULL, NULL, NULL, sock);
+    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+    if (!channel) {
+      SILC_LOG_ERROR(("Received key for non-existent channel"));
+      goto out;
     }
-    break;
+  }
 
-  case SILC_ID_SERVER:
-    {
-      SilcServerEntry idlist;
+  tmp = silc_channel_key_get_key(payload, &tmp_len);
+  if (!tmp) {
+    channel = NULL;
+    goto out;
+  }
 
-      /* Add the server to our local list. We are router and we keep
-        cell specific local database of all servers in the cell. */
-      idlist = silc_idlist_add_server(server->local_list, NULL, 0,
-                                     id, server->id_entry, NULL, NULL, 
-                                     NULL, NULL, NULL, sock);
-    }
-    break;
+  cipher = silc_channel_key_get_cipher(payload, NULL);;
+  if (!cipher) {
+    channel = NULL;
+    goto out;
+  }
 
-  case SILC_ID_CHANNEL:
-    /* Add the channel to our local list. We are router and we keep
-       cell specific local database of all channels in the cell. */
-    silc_idlist_add_channel(server->local_list, NULL, 0, id, 
-                           server->id_entry, NULL);
-    break;
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+    silc_cipher_free(channel->channel_key);
+  }
 
-  default:
+  /* Create new cipher */
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel = NULL;
     goto out;
-    break;
   }
 
+  /* Save the key */
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
+  memcpy(channel->key, tmp, tmp_len);
+  channel->channel_key->cipher->set_key(channel->channel_key->context, 
+                                       tmp, tmp_len);
+
  out:
-  silc_free(id_string);
+  if (id)
+    silc_free(id);
+  if (payload)
+    silc_channel_key_payload_free(payload);
+
+  return channel;
 }