A LOT updates. Cannot separate. :)
[silc.git] / apps / silcd / server.c
index 059535864952e758e2df71e54b0c823ffdae2a06..64b3319ba0b333718a7d6ea5307e332834068c4d 100644 (file)
  * servicing the SILC connections. This is also a SILC router as a router 
  * is also normal server.
  */
-/*
- * $Id$
- * $Log$
- * 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"
@@ -56,25 +35,10 @@ 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[];
+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
@@ -83,27 +47,17 @@ extern char server_version[];
 
 int silc_server_alloc(SilcServer *new_server)
 {
+  SilcServer server;
+
   SILC_LOG_DEBUG(("Allocating new server object"));
 
-  *new_server = silc_calloc(1, sizeof(**new_server));
-  if (*new_server == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new server object"));
-    return FALSE;
-  }
+  server = silc_calloc(1, sizeof(*server));
+  server->server_type = SILC_SERVER;
+  server->standalone = FALSE;
+  server->local_list = silc_calloc(1, sizeof(*server->local_list));
+  server->global_list = silc_calloc(1, sizeof(*server->global_list));
 
-  /* Set default values */
-  (*new_server)->server_name = NULL;
-  (*new_server)->server_type = SILC_SERVER;
-  (*new_server)->standalone = FALSE;
-  (*new_server)->id = NULL;
-  (*new_server)->io_queue = NULL;
-  (*new_server)->timeout_queue = NULL;
-  (*new_server)->local_list = silc_calloc(1, sizeof(SilcIDListObject));
-  (*new_server)->global_list = silc_calloc(1, sizeof(SilcIDListObject));
-  (*new_server)->rng = NULL;
-  (*new_server)->md5hash = NULL;
-  (*new_server)->sha1hash = NULL;
-  /*  (*new_server)->public_key = NULL;*/
+  *new_server = server;
 
   return TRUE;
 }
@@ -135,10 +89,9 @@ void silc_server_free(SilcServer server)
 
 int silc_server_init(SilcServer server)
 {
-  int *sock = NULL, sock_count, i;
+  int *sock = NULL, sock_count = 0, i;
   SilcServerID *id;
-  SilcServerList *id_entry;
-  SilcHashObject hash;
+  SilcServerEntry id_entry;
 
   SILC_LOG_DEBUG(("Initializing server"));
   assert(server);
@@ -200,16 +153,20 @@ int silc_server_init(SilcServer server)
        silc_pkcs_private_key_alloc("rsa", private_key, prv_len);
       
       /* XXX Save keys */
-      silc_pkcs_save_public_key("pubkey.pub", server->public_key);
-      silc_pkcs_save_private_key("privkey.prv", server->private_key, NULL);
+      silc_pkcs_save_public_key("pubkey.pub", server->public_key,
+                               SILC_PKCS_FILE_PEM);
+      silc_pkcs_save_private_key("privkey.prv", server->private_key, NULL,
+                                SILC_PKCS_FILE_BIN);
 
       memset(public_key, 0, pk_len);
       memset(private_key, 0, prv_len);
       silc_free(public_key);
       silc_free(private_key);
     } else {
-      silc_pkcs_load_public_key("pubkey.pub", &server->public_key);
-      silc_pkcs_load_private_key("privkey.prv", &server->private_key);
+      silc_pkcs_load_public_key("pubkey.pub", &server->public_key,
+                               SILC_PKCS_FILE_PEM);
+      silc_pkcs_load_private_key("privkey.prv", &server->private_key,
+                                SILC_PKCS_FILE_BIN);
     }
   }
 
@@ -231,6 +188,20 @@ int silc_server_init(SilcServer server)
     sock_count++;
   }
 
+  /* Initialize ID caches */
+  server->local_list->clients = silc_idcache_alloc(0);
+  server->local_list->servers = silc_idcache_alloc(0);
+  server->local_list->channels = silc_idcache_alloc(0);
+
+  /* XXX for now these are allocated for normal server as well as these
+     hold some global information that the server has fetched from its
+     router. For router these are used as they are supposed to be used
+     on router. The XXX can be remoevd later if this is the way we are
+     going to do this in the normal server as well. */
+  server->global_list->clients = silc_idcache_alloc(0);
+  server->global_list->servers = silc_idcache_alloc(0);
+  server->global_list->channels = silc_idcache_alloc(0);
+
   /* Allocate the entire socket list that is used in server. Eventually 
      all connections will have entry in this table (it is a table of 
      pointers to the actual object that is allocated individually 
@@ -259,13 +230,16 @@ int silc_server_init(SilcServer server)
        beacuse we haven't established a route yet. It will be done later. 
        For now, NULL is sent as router. This allocates new entry to
        the ID list. */
-    silc_idlist_add_server(&server->local_list->servers, 
-                          server->config->server_info->server_name,
-                          server->server_type, server->id, NULL,
-                          server->send_key, server->receive_key,
-                          NULL, NULL, &id_entry);
-    if (!id_entry)
+    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);
+    if (!id_entry) {
+      SILC_LOG_ERROR(("Could not add ourselves to cache"));
       goto err0;
+    }
     
     /* Add ourselves also to the socket table. The entry allocated above
        is sent as argument for fast referencing in the future. */
@@ -299,9 +273,12 @@ 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
@@ -351,6 +328,8 @@ void silc_server_stop(SilcServer server)
   silc_schedule_stop();
   silc_schedule_uninit();
 
+  silc_server_protocols_unregister();
+
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
@@ -644,8 +623,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
-  SilcServerList *id_entry;
-  SilcIDListUnknown *conn_data;
+  SilcServerEntry id_entry;
+  SilcUnknownEntry conn_data;
   SilcBuffer packet;
   unsigned char *id_string;
 
@@ -706,20 +685,21 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add the connected router to local server list */
   server->standalone = FALSE;
-  conn_data = (SilcIDListUnknown *)sock->user_data;
-  silc_idlist_add_server(&server->local_list->servers, 
-                        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, &id_entry);
-
-  id_entry->hmac_key = conn_data->hmac_key;
-  id_entry->hmac_key_len = conn_data->hmac_key_len;
-  id_entry->connection = sock;
-  sock->user_data = (void *)id_entry;
-  sock->type = SILC_SOCKET_TYPE_ROUTER;
-  server->id_entry->router = id_entry;
-
+  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;
+  }
+    
   /* Free the temporary connection data context from key exchange */
   silc_free(conn_data);
 
@@ -848,7 +828,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     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");
     return;
@@ -914,7 +895,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     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");
     return;
@@ -924,67 +906,71 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
-      SilcClientList *id_entry = NULL;
-      SilcIDListUnknown *conn_data = sock->user_data;
+      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,
                     sock->ip));
 
-      /* Add the client to the client ID list. We have not created the
-        client ID for the client yet. This is done when client registers
-        itself by sending NEW_CLIENT packet. */
-      silc_idlist_add_client(&server->local_list->clients, 
-                            NULL, NULL, NULL, NULL, NULL,
-                            conn_data->send_key, conn_data->receive_key, 
-                            conn_data->pkcs, conn_data->hmac, &id_entry);
-
-      id_entry->hmac_key = conn_data->hmac_key;
-      id_entry->hmac_key_len = conn_data->hmac_key_len;
-      id_entry->connection = sock;
+      /* 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);
+      if (!client) {
+       SILC_LOG_ERROR(("Could not add new client to cache"));
+       silc_free(conn_data);
+       break;
+      }
 
       /* Free the temporary connection data context from key exchange */
       silc_free(conn_data);
 
-      /* Mark the entry to the ID list to the socket connection for
-        fast referencing in the future. */
-      sock->user_data = (void *)id_entry;
+      /* Add to sockets internal pointer for fast referencing */
+      sock->user_data = (void *)client;
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     {
-      SilcServerList *id_entry;
-      SilcIDListUnknown *conn_data = sock->user_data;
-      
+      SilcServerEntry new_server;
+      SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
+
       SILC_LOG_DEBUG(("Remote host is %s", 
                      sock->type == SILC_SOCKET_TYPE_SERVER ? 
                      "server" : "router"));
-      
       SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
                     sock->ip, sock->type == SILC_SOCKET_TYPE_SERVER ? 
                     "server" : "router"));
 
-      /* Add the server to the ID list. We don't have the server's ID
-        yet but we will receive it after the server sends NEW_SERVER
-        packet to us. */
-      silc_idlist_add_server(&server->local_list->servers, 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, &id_entry);
-
-      id_entry->hmac_key = conn_data->hmac_key;
-      id_entry->hmac_key_len = conn_data->hmac_key_len;
-      id_entry->connection = sock;
-
-      /* Free the temporary connection data context from key exchange */
+      /* Add the server into server cache. The server name and Server ID
+        is updated after we have received NEW_SERVER packet from the
+        server. */
+      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);
+      if (!new_server) {
+       SILC_LOG_ERROR(("Could not add new server to cache"));
+       silc_free(conn_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);
 
-      /* Mark the entry to the ID list to the socket connection for
-        fast referencing in the future. */
-      sock->user_data = (void *)id_entry;
+      /* Add to sockets internal pointer for fast referencing */
+      sock->user_data = (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
@@ -1014,6 +1000,35 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   sock->protocol = NULL;
 }
 
+/* Internal routine that sends packet or marks packet to be sent. This
+   is used directly only in special cases. Normal cases should use
+   silc_server_packet_send. Returns < 0 error. */
+
+static int silc_server_packet_send_real(SilcServer server,
+                                       SilcSocketConnection sock,
+                                       int force_send)
+{
+  int ret;
+
+  /* Send the packet */
+  ret = silc_packet_send(sock, force_send);
+  if (ret != -2)
+    return ret;
+
+  /* Mark that there is some outgoing data available for this connection. 
+     This call sets the connection both for input and output (the input
+     is set always and this call keeps the input setting, actually). 
+     Actual data sending is performed by silc_server_packet_process. */
+  SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock);
+
+  /* Mark to socket that data is pending in outgoing buffer. This flag
+     is needed if new data is added to the buffer before the earlier
+     put data is sent to the network. */
+  SILC_SET_OUTBUF_PENDING(sock);
+
+  return 0;
+}
+
 typedef struct {
   SilcPacketContext *packetdata;
   SilcServer server;
@@ -1029,7 +1044,9 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 {
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = server->sockets[fd];
-  int ret, packetlen, paddedlen;
+  SilcCipher cipher = NULL;
+  SilcHmac hmac = NULL;
+  int ret;
 
   SILC_LOG_DEBUG(("Processing packet"));
 
@@ -1041,18 +1058,13 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
       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"));
-
     /* 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. 
@@ -1068,22 +1080,10 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   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"));
+    ret = silc_packet_receive(sock);
+    if (ret < 0)
       return;
-    }
     
     /* EOF */
     if (ret == 0) {
@@ -1113,489 +1113,127 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
       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;
-    }
+    switch (sock->type) {
+    case SILC_SOCKET_TYPE_CLIENT:
+      {
+       SilcClientEntry clnt = (SilcClientEntry)sock->user_data;
+       if (!clnt)
+         break;
 
-    /* Decrypt a packet coming from client. */
-    if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientList *clnt = (SilcClientList *)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) {
-      SilcServerList *srvr = (SilcServerList *)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 {
+       clnt->last_receive = time(NULL);
 
-       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;
+       cipher = clnt->receive_key;
+       hmac = clnt->hmac;
+       break;
       }
-    }
+    case SILC_SOCKET_TYPE_SERVER:
+    case SILC_SOCKET_TYPE_ROUTER:
+      {
+       SilcServerEntry srvr = (SilcServerEntry)sock->user_data;
+       if (!srvr)
+         break;
 
-    /* Decrypt a packet coming from client. */
-    if (sock->type == SILC_SOCKET_TYPE_UNKNOWN) {
-      SilcIDListUnknown *conn_data = (SilcIDListUnknown *)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;
-      }
+       srvr->last_receive = time(NULL);
 
-      silc_buffer_clear(sock->inbuf);
+       cipher = srvr->receive_key;
+       hmac = srvr->hmac;
+       break;
+      }
+    case SILC_SOCKET_TYPE_UNKNOWN:
+      {
+       SilcUnknownEntry conn_data = (SilcUnknownEntry)sock->user_data;
+       if (!conn_data)
+         break;
 
-      /* 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, 
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+       cipher = conn_data->receive_key;
+       hmac = conn_data->hmac;
+       break;
+      }
+    default:
       return;
     }
+    /* Process the packet. This will call the parser that will then
+       decrypt and parse the packet. */
+    silc_packet_receive_process(sock, cipher, hmac,
+                               silc_server_packet_parse, server);
   }
-
-  SILC_LOG_ERROR(("Weird, nothing happened - ignoring"));
 }
 
-/* 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;
-
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-      hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-      hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
-      hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
-    }
-  }
-
-  /* Check MAC */
-  if (hmac) {
-    int headlen = buffer->data - buffer->head;
-    unsigned char *packet_mac, mac[32];
-    
-    SILC_LOG_DEBUG(("Verifying MAC"));
+  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
+  SilcServer server = (SilcServer)parse_ctx->context;
+  SilcSocketConnection sock = parse_ctx->sock;
+  SilcPacketContext *packet = parse_ctx->packet;
+  SilcBuffer buffer = packet->buffer;
+  int ret;
 
-    mac_len = hmac->hash->hash->hash_len;
+  SILC_LOG_DEBUG(("Start"));
 
-    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;
-    }
-    
-    SILC_LOG_DEBUG(("MAC is Ok"));
-    memset(mac, 0, sizeof(mac));
+  /* Decrypt the received packet */
+  ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac, 
+                           buffer, packet);
+  if (ret < 0)
+    goto out;
 
-    silc_buffer_pull(buffer, headlen);
+  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);
   }
-  
-  return TRUE;
-}
-
-/* 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. */
 
-static int silc_server_packet_decrypt_rest(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 = ((SilcClientList *)sock->user_data)->receive_key;
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      session_key = ((SilcServerList *)sock->user_data)->receive_key;
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key;
-      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
-    }
-  }
+  if (ret == SILC_PACKET_NONE)
+    goto out;
   
-  /* 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);
-  }
+  /* Parse the incoming packet type */
+  silc_server_packet_parse_type(server, sock, packet);
 
-  return TRUE;
+ out:
+  silc_buffer_clear(sock->inbuf);
+  if (packet->src_id)
+    silc_free(packet->src_id);
+  if (packet->dst_id)
+    silc_free(packet->dst_id);
+  silc_free(packet);
+  silc_free(parse_ctx);
 }
 
-/* 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. */
+/* Parser callback called by silc_packet_receive_process. This merely
+   registers timeout that will handle the actual parsing whem appropriate. */
 
-static int silc_server_packet_decrypt_rest_special(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 = ((SilcClientList *)sock->user_data)->receive_key;
-      hmac = ((SilcClientList *)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 = ((SilcServerList *)sock->user_data)->receive_key;
-      hmac = ((SilcServerList *)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 = ((SilcIDListUnknown *)sock->user_data)->receive_key;
-      hmac = ((SilcIDListUnknown *)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;
+    return;
   }
-  
-  /* 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
@@ -1628,7 +1266,18 @@ void silc_server_packet_parse_type(SilcServer server,
     }
     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"));
@@ -1663,37 +1312,11 @@ void silc_server_packet_parse_type(SilcServer server,
      * Command packets
      */
   case SILC_PACKET_COMMAND:
-    {
-      /*
-       * Recived command. Allocate command context and execute the command.
-       */
-      SilcServerCommandContext ctx;
-
-      SILC_LOG_DEBUG(("Command packet"));
-
-      /* Router cannot send command packet */
-      if (sock->type == SILC_SOCKET_TYPE_ROUTER)
-       break;
-
-      /* Allocate command context. This must be free'd by the
-        command routine receiving it. */
-      ctx = silc_calloc(1, sizeof(*ctx));
-      ctx->server = server;
-      ctx->sock = sock;
-      ctx->packet = packet;    /* Save original packet */
-
-      /* Parse the command payload in the packet */
-      ctx->payload = silc_command_parse_payload(buffer);
-      if (!ctx->payload) {
-       SILC_LOG_ERROR(("Bad command payload, packet dropped"));
-       silc_free(ctx);
-       return;
-      }
-
-      /* Execute command. If this fails the packet is dropped. */
-      SILC_SERVER_COMMAND_EXEC(ctx);
-      silc_buffer_free(buffer);
-    }
+    /*
+     * Recived command. Allocate command context and execute the command.
+     */
+    SILC_LOG_DEBUG(("Command packet"));
+    silc_server_command_process(server, sock, packet);
     break;
 
   case SILC_PACKET_COMMAND_REPLY:
@@ -1849,100 +1472,52 @@ 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;
 
-  default:
-    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+  case SILC_PACKET_NEW_CHANNEL:
     break;
-  }
-  
-}
 
-/* 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. */
+  case SILC_PACKET_NEW_CHANNEL_USER:
+    break;
 
-static int silc_server_packet_send_real(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       int force_send)
-{
-  /* 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"));
-
-  /* Mark that there is some outgoing data available for this connection. 
-     This call sets the connection both for input and output (the input
-     is set always and this call keeps the input setting, actually). 
-     Actual data sending is performed by silc_server_packet_process. */
-  SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock);
-
-  /* Mark to socket that data is pending in outgoing buffer. This flag
-     is needed if new data is added to the buffer before the earlier
-     put data is sent to the network. */
-  SILC_SET_OUTBUF_PENDING(sock);
-
-  return 0;
-}
+  case SILC_PACKET_NEW_CHANNEL_LIST:
+    break;
 
-/* Prepare outgoing data buffer for packet sending. This is internal
-   routine and must always be called before sending any packets out. */
+  case SILC_PACKET_NEW_CHANNEL_USER_LIST:
+    break;
 
-static void silc_server_packet_send_prepare(SilcServer server, 
-                                           SilcSocketConnection sock,
-                                           unsigned int header_len,
-                                           unsigned int padlen,
-                                           unsigned int data_len)
-{
-  int totlen, oldlen;
+  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;
 
-  totlen = header_len + padlen + data_len;
+  case SILC_PACKET_REMOVE_ID:
+    break;
 
-  /* 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. */
+  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;
 
-      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);
-    }
+  default:
+    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+    break;
   }
+  
 }
 
 /* Assembles a new packet to be sent out to network. This doesn't actually
@@ -1962,18 +1537,21 @@ void silc_server_packet_send(SilcServer server,
   void *dst_id = NULL;
   SilcIdType dst_id_type = SILC_ID_NONE;
 
+  if (!sock)
+    return;
+
   /* Get data used in the packet sending, keys and stuff */
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
-    if (((SilcClientList *)sock->user_data)->id) {
-      dst_id = ((SilcClientList *)sock->user_data)->id;
+    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 (((SilcServerList *)sock->user_data)->id) {
-      dst_id = ((SilcServerList *)sock->user_data)->id;
+    if (((SilcServerEntry)sock->user_data)->id) {
+      dst_id = ((SilcServerEntry)sock->user_data)->id;
       dst_id_type = SILC_ID_SERVER;
     }
     break;
@@ -2005,10 +1583,6 @@ void silc_server_packet_send_dest(SilcServer server,
   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;
 
@@ -2018,38 +1592,23 @@ void silc_server_packet_send_dest(SilcServer server,
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
     if (sock->user_data) {
-      cipher = ((SilcClientList *)sock->user_data)->send_key;
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
-      }
+      cipher = ((SilcClientEntry)sock->user_data)->send_key;
+      hmac = ((SilcClientEntry)sock->user_data)->hmac;
     }
     break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     if (sock->user_data) {
-      cipher = ((SilcServerList *)sock->user_data)->send_key;
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
-      }
+      cipher = ((SilcServerEntry)sock->user_data)->send_key;
+      hmac = ((SilcServerEntry)sock->user_data)->hmac;
     }
     break;
   default:
     if (sock->user_data) {
       /* We don't know what type of connection this is thus it must
         be in authentication phase. */
-      cipher = ((SilcIDListUnknown *)sock->user_data)->send_key;
-      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
-      }
+      cipher = ((SilcUnknownEntry)sock->user_data)->send_key;
+      hmac = ((SilcUnknownEntry)sock->user_data)->hmac;
     }
     break;
   }
@@ -2074,12 +1633,12 @@ void silc_server_packet_send_dest(SilcServer server,
   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_packet_send_prepare(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));
 
@@ -2092,21 +1651,9 @@ void silc_server_packet_send_dest(SilcServer server,
   /* 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));
-  }
-
   /* 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_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
 
   SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len),
                   sock->outbuf->data, sock->outbuf->len);
@@ -2134,10 +1681,6 @@ void silc_server_packet_forward(SilcServer server,
 {
   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;
 
   SILC_LOG_DEBUG(("Forwarding packet"));
 
@@ -2145,25 +1688,15 @@ void silc_server_packet_forward(SilcServer server,
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
     if (sock->user_data) {
-      cipher = ((SilcClientList *)sock->user_data)->send_key;
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
-      }
+      cipher = ((SilcClientEntry )sock->user_data)->send_key;
+      hmac = ((SilcClientEntry )sock->user_data)->hmac;
     }
     break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     if (sock->user_data) {
-      cipher = ((SilcServerList *)sock->user_data)->send_key;
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
-      }
+      cipher = ((SilcServerEntry )sock->user_data)->send_key;
+      hmac = ((SilcServerEntry )sock->user_data)->hmac;
     }
     break;
   default:
@@ -2173,7 +1706,7 @@ void silc_server_packet_forward(SilcServer server,
   }
 
   /* Prepare outgoing data buffer for packet sending */
-  silc_server_packet_send_prepare(server, sock, 0, 0, data_len);
+  silc_packet_send_prepare(sock, 0, 0, data_len);
 
   /* Mungle the packet flags and add the FORWARDED flag */
   if (data)
@@ -2183,21 +1716,9 @@ void silc_server_packet_forward(SilcServer server,
   if (data && data_len)
     silc_buffer_put(sock->outbuf, data, data_len);
 
-  /* 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));
-  }
-
   /* 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_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
 
   SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
                   sock->outbuf->data, sock->outbuf->len);
@@ -2206,13 +1727,61 @@ void silc_server_packet_forward(SilcServer server,
   silc_server_packet_send_real(server, sock, force_send);
 }
 
+/* Internal routine to actually create the channel packet and send it
+   to network. This is common function in channel message sending. If
+   `channel_message' is TRUE this encrypts the message as it is strictly
+   a channel message. If FALSE normal encryption process is used. */
+
+static void
+silc_server_packet_send_to_channel_real(SilcServer server,
+                                       SilcSocketConnection sock,
+                                       SilcPacketContext *packet,
+                                       SilcCipher cipher,
+                                       SilcHmac hmac,
+                                       unsigned char *data,
+                                       unsigned int data_len,
+                                       int channel_message,
+                                       int force_send)
+{
+  packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packet->src_id_len + packet->dst_id_len;
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packet->src_id_len + 
+                          packet->dst_id_len,
+                          packet->padlen,
+                          data_len);
+
+  packet->buffer = sock->outbuf;
+
+  /* Put the data to buffer, assemble and encrypt the packet. The packet
+     is encrypted with normal session key shared with the client. */
+  silc_buffer_put(sock->outbuf, data, data_len);
+  silc_packet_assemble(packet);
+  if (channel_message)
+    silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
+                       packet->src_id_len + packet->dst_id_len +
+                       packet->padlen);
+  else
+    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+    
+  SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_server_packet_send_real(server, sock, force_send);
+}
+
 /* 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. */
 
 void silc_server_packet_send_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
+                                       SilcChannelEntry channel,
+                                       SilcPacketType type,
                                        unsigned char *data,
                                        unsigned int data_len,
                                        int force_send)
@@ -2220,39 +1789,22 @@ void silc_server_packet_send_to_channel(SilcServer server,
   int i;
   SilcSocketConnection sock = NULL;
   SilcPacketContext packetdata;
-  SilcClientList *client = NULL;
-  SilcServerList **routed = NULL;
+  SilcClientEntry client = NULL;
+  SilcServerEntry *routed = NULL;
+  SilcChannelClientEntry chl;
   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)
+  /* This doesn't send channel message packets */
+  if (type == SILC_PACKET_CHANNEL_MESSAGE)
     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);
+  SILC_LOG_DEBUG(("Sending packet to channel"));
 
   /* Set the packet context pointers. */
   packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+  packetdata.type = type;
   packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
   packetdata.src_id_len = SILC_ID_SERVER_LEN;
   packetdata.src_id_type = SILC_ID_SERVER;
@@ -2260,71 +1812,33 @@ void silc_server_packet_send_to_channel(SilcServer server,
   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));
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
 
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
   if (server->server_type == SILC_SERVER && !server->standalone &&
       channel->global_users) {
-    SilcServerList *router;
+    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);
+    SILC_LOG_DEBUG(("Sending channel message to router for routing"));
 
-    /* Now actually send the packet */
-    silc_server_packet_send_real(server, sock, force_send);
+    silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                           cipher, hmac, data,
+                                           data_len, FALSE, force_send);
   }
 
   /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     /* If client has router set it is not locally connected client and
        we will route the message to the router set in the client. */
@@ -2334,55 +1848,19 @@ void silc_server_packet_send_to_channel(SilcServer server,
       /* Check if we have sent the packet to this route already */
       for (k = 0; k < routed_count; k++)
        if (routed[k] == client->router)
-         continue;
+         break;
+      if (k < routed_count)
+       continue;
 
       /* Get data used in packet header encryption, keys and stuff. */
       sock = (SilcSocketConnection)client->router->connection;
       cipher = client->router->send_key;
       hmac = client->router->hmac;
-      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);
 
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
+      /* Send the packet */
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             cipher, hmac, data,
+                                             data_len, FALSE, force_send);
 
       /* We want to make sure that the packet is routed to same router
         only once. Mark this route as sent route. */
@@ -2403,49 +1881,11 @@ void silc_server_packet_send_to_channel(SilcServer server,
       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);
+      /* Send the packet */
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             cipher, hmac, data,
+                                             data_len, FALSE, force_send);
     }
   }
 
@@ -2453,7 +1893,6 @@ void silc_server_packet_send_to_channel(SilcServer server,
     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.
@@ -2467,7 +1906,7 @@ void silc_server_packet_send_to_channel(SilcServer server,
 
 void silc_server_packet_relay_to_channel(SilcServer server,
                                         SilcSocketConnection sender_sock,
-                                        SilcChannelList *channel,
+                                        SilcChannelEntry channel,
                                         void *sender, 
                                         SilcIdType sender_type,
                                         unsigned char *data,
@@ -2477,20 +1916,15 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   int i, found = FALSE;
   SilcSocketConnection sock = NULL;
   SilcPacketContext packetdata;
-  SilcClientList *client = NULL;
-  SilcServerList **routed = NULL;
+  SilcClientEntry client = NULL;
+  SilcServerEntry *routed = NULL;
+  SilcChannelClientEntry chl;
   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;
@@ -2509,7 +1943,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
      first to our router for further routing. */
   if (server->server_type == SILC_SERVER && !server->standalone &&
       channel->global_users) {
-    SilcServerList *router;
+    SilcServerEntry router;
 
     router = server->id_entry->router;
 
@@ -2520,57 +1954,19 @@ void silc_server_packet_relay_to_channel(SilcServer server,
       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);
+
+      SILC_LOG_DEBUG(("Sending channel message to router for routing"));
+
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             cipher, hmac, data,
+                                             data_len, TRUE, force_send);
     }
   }
 
   /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     if (client) {
 
@@ -2595,55 +1991,19 @@ void silc_server_packet_relay_to_channel(SilcServer server,
        /* Check if we have sent the packet to this route already */
        for (k = 0; k < routed_count; k++)
          if (routed[k] == client->router)
-           continue;
+           break;
+       if (k < routed_count)
+         continue;
        
        /* Get data used in packet header encryption, keys and stuff. */
        sock = (SilcSocketConnection)client->router->connection;
        cipher = client->router->send_key;
        hmac = client->router->hmac;
-       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);
+
+       /* Send the packet */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               cipher, hmac, data,
+                                               data_len, TRUE, force_send);
        
        /* We want to make sure that the packet is routed to same router
           only once. Mark this route as sent route. */
@@ -2654,59 +2014,21 @@ void silc_server_packet_relay_to_channel(SilcServer server,
        
        continue;
       }
-      
+
       /* 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);
+      /* Send the packet */
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             cipher, hmac, data,
+                                             data_len, TRUE, force_send);
     }
   }
 
@@ -2718,10 +2040,11 @@ void silc_server_packet_relay_to_channel(SilcServer server,
    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. */
+   the client, this means these are not _to the channel_ but _to the client_
+   on the channel. */
 
 void silc_server_packet_send_local_channel(SilcServer server,
-                                          SilcChannelList *channel,
+                                          SilcChannelEntry channel,
                                           SilcPacketType type,
                                           SilcPacketFlags flags,
                                           unsigned char *data,
@@ -2729,14 +2052,16 @@ void silc_server_packet_send_local_channel(SilcServer server,
                                           int force_send)
 {
   int i;
-  SilcClientList *client;
+  SilcClientEntry client;
+  SilcChannelClientEntry chl;
   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;
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     if (client) {
       sock = (SilcSocketConnection)client->connection;
@@ -2760,19 +2085,15 @@ void silc_server_packet_relay_command_reply(SilcServer server,
                                            SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcClientList *client;
+  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))
+      sock->type != SILC_SOCKET_TYPE_ROUTER)
     goto out;
 
   /* Destination must be client */
@@ -2785,42 +2106,25 @@ void silc_server_packet_relay_command_reply(SilcServer server,
   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->clients, id);
+  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_packet_send_prepare(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 */
+
+  /* Encrypt packet */
   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);
+    silc_packet_encrypt(client->send_key, client->hmac, 
+                       dst_sock->outbuf, buffer->len);
     
   /* Send the packet */
   silc_server_packet_send_real(server, dst_sock, FALSE);
@@ -2880,7 +2184,7 @@ void silc_server_disconnect_remote(SilcServer server,
 }
 
 /* Free's user_data pointer from socket connection object. As this 
-   pointer maybe anything we wil switch here to find the corrent
+   pointer maybe anything we wil switch here to find the correct
    data type and free it the way it needs to be free'd. */
 
 void silc_server_free_sock_user_data(SilcServer server, 
@@ -2888,26 +2192,17 @@ void silc_server_free_sock_user_data(SilcServer server,
 {
   SILC_LOG_DEBUG(("Start"));
 
-#define LCC(x) server->local_list->client_cache[(x) - 32]
-#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
-
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
-      SilcClientList *user_data = (SilcClientList *)sock->user_data;
+      SilcClientEntry user_data = (SilcClientEntry )sock->user_data;
 
       /* Remove client from all channels */
       silc_server_remove_from_channels(server, sock, user_data);
 
-      /* Clear ID cache */
-      if (user_data->nickname && user_data->id)
-       silc_idcache_del_by_id(LCC(user_data->nickname[0]),
-                              LCCC(user_data->nickname[0]),
-                              SILC_ID_CLIENT, user_data->id);
-
       /* Free the client entry and everything in it */
       /* XXX must take some info to history before freeing */
-      silc_idlist_del_client(&server->local_list->clients, user_data);
+      silc_idlist_del_client(server->local_list, user_data);
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
@@ -2919,7 +2214,7 @@ void silc_server_free_sock_user_data(SilcServer server,
     break;
   default:
     {
-      SilcIDListUnknown *user_data = (SilcIDListUnknown *)sock->user_data;
+      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
 
       if (user_data->send_key)
        silc_cipher_free(user_data->send_key);
@@ -2948,13 +2243,16 @@ void silc_server_free_sock_user_data(SilcServer server,
 
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
-                                     SilcClientList *client)
+                                     SilcClientEntry client)
 {
   int i, k;
-  SilcChannelList *channel;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer chidp, clidp;
+
+  SILC_LOG_DEBUG(("Start"));
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Remove the client from all channels. The client is removed from
      the channels' user list. */
@@ -2963,54 +2261,70 @@ void silc_server_remove_from_channels(SilcServer server,
     if (!channel)
       continue;
 
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+
     /* Remove from channel */
-    for (k = 0; k < channel->user_list_count; k++) {
-      if (channel->user_list[k].client == client) {
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      if (chl->client == client) {
 
        /* If this client is last one on the channel the channel
           is removed all together. */
-       if (channel->user_list_count == 1) {
-         silc_idcache_del_by_id(LCC(channel->channel_name[0]),
-                                LCCC(channel->channel_name[0]),
-                                SILC_ID_CHANNEL, channel->id);
-         silc_idlist_del_channel(&server->local_list->channels, channel);
+       if (silc_list_count(channel->user_list) == 1) {
+
+         /* However, if the channel has marked global users then the 
+            channel is not created locally, and this does not remove the
+            channel globally from SILC network, in this case we will
+            notify that this client has left the channel. */
+         if (channel->global_users)
+           silc_server_send_notify_to_channel(server, channel,
+                                              SILC_NOTIFY_TYPE_SIGNOFF, 1,
+                                              clidp->data, clidp->len);
+
+         silc_idlist_del_channel(server->local_list, channel);
          break;
        }
 
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
+       silc_list_del(channel->user_list, chl);
+       silc_free(chl);
 
        /* 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);
+                                          SILC_NOTIFY_TYPE_SIGNOFF, 1,
+                                          clidp->data, clidp->len);
       }
     }
+
+    silc_buffer_free(chidp);
   }
 
   if (client->channel_count)
     silc_free(client->channel);
   client->channel = NULL;
-#undef LCC
-#undef LCCC
+  silc_buffer_free(clidp);
 }
 
 /* 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. */
+   last client leaves the channel. If `notify' is FALSE notify messages
+   are not sent. */
 
 int silc_server_remove_from_one_channel(SilcServer server, 
                                        SilcSocketConnection sock,
-                                       SilcChannelList *channel,
-                                       SilcClientList *client)
+                                       SilcChannelEntry channel,
+                                       SilcClientEntry client,
+                                       int notify)
 {
   int i, k;
-  SilcChannelList *ch;
+  SilcChannelEntry ch;
+  SilcChannelClientEntry chl;
+  SilcBuffer clidp;
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+  SILC_LOG_DEBUG(("Start"));
+
+  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. */
@@ -3019,48 +2333,51 @@ int silc_server_remove_from_one_channel(SilcServer server,
     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) {
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      if (chl->client == client) {
        
        /* If this client is last one on the channel the channel
           is removed all together. */
-       if (channel->user_list_count == 1) {
-         silc_idcache_del_by_id(LCC(channel->channel_name[0]),
-                                LCCC(channel->channel_name[0]),
-                                SILC_ID_CHANNEL, channel->id);
-         silc_idlist_del_channel(&server->local_list->channels, channel);
+       if (silc_list_count(channel->user_list) == 1) {
+         /* Notify about leaving client if this channel has global users,
+            ie. the channel is not created locally. */
+         if (notify && channel->global_users)
+           silc_server_send_notify_to_channel(server, channel,
+                                              SILC_NOTIFY_TYPE_LEAVE, 1,
+                                              clidp->data, clidp->len);
+
+         silc_idlist_del_channel(server->local_list, channel);
+         silc_buffer_free(clidp);
          return FALSE;
        }
        
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
+       silc_list_del(channel->user_list, chl);
+       silc_free(chl);
 
        /* 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);
+       if (notify)
+         silc_server_send_notify_to_channel(server, channel,
+                                            SILC_NOTIFY_TYPE_LEAVE, 1,
+                                            clidp->data, clidp->len);
       }
     }
   }
 
+  silc_buffer_free(clidp);
   return TRUE;
-#undef LCC
-#undef LCCC
 }
 
 /* Returns TRUE if the given client is on the channel.  FALSE if not. 
    This works because we assure that the user list on the channel is
    always in up to date thus we can only check the channel list from 
    `client' which is faster than checking the user list from `channel'. */
-/* XXX This really is utility function and should be in eg. serverutil.c */
 
-int silc_server_client_on_channel(SilcClientList *client,
-                                 SilcChannelList *channel)
+int silc_server_client_on_channel(SilcClientEntry client,
+                                 SilcChannelEntry channel)
 {
   int i;
 
@@ -3090,114 +2407,37 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
 }
 
 /* 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
+   destination. If the private message key does not exist then the message
    is re-encrypted, otherwise we just pass it along. */
+
 static void 
 silc_server_private_message_send_internal(SilcServer server,
                                          SilcSocketConnection dst_sock,
-                                         SilcServerList *router,
+                                         SilcCipher cipher,
+                                         SilcHmac hmac,
                                          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_packet_send_prepare(dst_sock, 0, 0, buffer->len);
     silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-  }
-}
-
-/* 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,
-                                      SilcClientList *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);
+    /* Re-encrypt packet */
+    silc_packet_encrypt(cipher, hmac, dst_sock->outbuf, buffer->len);
     
     /* Send the packet */
     silc_server_packet_send_real(server, dst_sock, FALSE);
+
   } else {
     /* Key exist so just send it */
     silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
                     + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
+    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
     silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
     silc_server_packet_send_real(server, dst_sock, FALSE);
   }
@@ -3215,31 +2455,27 @@ void silc_server_private_message(SilcServer server,
 {
   SilcBuffer buffer = packet->buffer;
   SilcClientID *id;
-  SilcServerList *router;
+  SilcServerEntry router;
   SilcSocketConnection dst_sock;
-  SilcClientList *client;
+  SilcClientEntry client;
 
   SILC_LOG_DEBUG(("Start"));
 
   if (!packet->dst_id) {
-    SILC_LOG_DEBUG(("Bad Client ID in private message packet"));
+    SILC_LOG_ERROR(("Bad Client ID in private message packet, dropped"));
     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"));
+    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
     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->clients, id);
+  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;
@@ -3247,13 +2483,23 @@ void silc_server_private_message(SilcServer server,
     /* If we are router and the client has router then the client is in
        our cell but not directly connected to us. */
     if (server->server_type == SILC_ROUTER && client->router) {
+      /* We are of course in this case the client's router thus the real
+        "router" of the client is the server who owns the client. Thus
+        we will send the packet to that server. */
+      router = (SilcServerEntry)dst_sock->user_data;
+      //      assert(client->router == server->id_entry);
+
       silc_server_private_message_send_internal(server, dst_sock,
-                                               client->router, packet);
+                                               router->send_key,
+                                               router->hmac,
+                                               packet);
       goto out;
     }
 
     /* Seems that client really is directly connected to us */
-    silc_server_private_message_send_local(server, dst_sock, client, packet);
+    silc_server_private_message_send_internal(server, dst_sock, 
+                                             client->send_key,
+                                             client->hmac, packet);
     goto out;
   }
 
@@ -3261,22 +2507,29 @@ void silc_server_private_message(SilcServer server,
      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);
+    if (router) {
+      dst_sock = (SilcSocketConnection)router->connection;
+      silc_server_private_message_send_internal(server, dst_sock, 
+                                               router->send_key,
+                                               router->hmac, 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) {
+    dst_sock = silc_server_get_route(server, id, SILC_ID_CLIENT);
+    router = (SilcServerEntry)dst_sock->user_data;
 
     /* 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);
+    if (router)
+      silc_server_private_message_send_internal(server, dst_sock, 
+                                               router->send_key,
+                                               router->hmac, packet);
+
     goto out;
   }
 
@@ -3287,45 +2540,22 @@ void silc_server_private_message(SilcServer server,
   silc_buffer_free(buffer);
 }
 
-SilcChannelList *silc_find_channel(SilcServer server, SilcChannelID *id)
-{
-  int i;
-  SilcIDCache *id_cache;
-
-#define LCC(x) server->local_list->channel_cache[(x)]
-#define LCCC(x) server->local_list->channel_cache_count[(x)]
-
-  for (i = 0; i < 96; i++) {
-    if (LCC(i) == NULL)
-      continue;
-    if (silc_idcache_find_by_id(LCC(i), LCCC(i), (void *)id, 
-                               SILC_ID_CHANNEL, &id_cache))
-      return (SilcChannelList *)id_cache->context;
-  }
-  
-  return NULL;
-#undef LCC
-#undef LCCC
-}
-
-/* Process received channel message. */
+/* Process received channel message. The message can be originated from
+   client or server. */
 
 void silc_server_channel_message(SilcServer server,
                                 SilcSocketConnection sock,
                                 SilcPacketContext *packet)
 {
-  SilcChannelList *channel = NULL;
-  SilcClientList *client = NULL;
+  SilcChannelEntry channel = NULL;
+  SilcChannelClientEntry chl;
+  SilcClientEntry client = NULL;
   SilcChannelID *id = NULL;
-  SilcClientID *sender = NULL;
+  void *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) {
@@ -3336,21 +2566,27 @@ void silc_server_channel_message(SilcServer server,
 
   /* Find channel entry */
   id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-  channel = silc_find_channel(server, id);
+  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 */
+  /* See that this client is on the channel. If the message is coming
+     from router we won't do the check as the message is from client that
+     we don't know about. Also, if the original sender is not client
+     (as it can be server as well) we don't do the check. */
   sender = silc_id_str2id(packet->src_id, packet->src_id_type);
-  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 (sock->type != SILC_SOCKET_TYPE_ROUTER && 
+      packet->src_id_type == SILC_ID_CLIENT) {
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
+       break;
+    }
+    if (chl == SILC_LIST_END)
+      goto out;
   }
-  if (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. */
@@ -3379,10 +2615,11 @@ void silc_server_channel_key(SilcServer server,
   SilcBuffer buffer = packet->buffer;
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
-  SilcChannelList *channel;
-  SilcClientList *client;
-  unsigned char *key;
-  unsigned int key_len;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcClientEntry client;
+  unsigned char *tmp;
+  unsigned int tmp_len;
   char *cipher;
   int i;
 
@@ -3391,42 +2628,46 @@ void silc_server_channel_key(SilcServer server,
     goto out;
 
   /* Decode channel key payload */
-  payload = silc_channel_key_parse_payload(buffer);
+  payload = silc_channel_key_payload_parse(buffer);
   if (!payload) {
     SILC_LOG_ERROR(("Bad channel key payload, dropped"));
-    SILC_LOG_DEBUG(("Bad channel key payload, dropped"));
+    goto out;
   }
 
   /* Get channel ID */
-  id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL);
+  tmp = silc_channel_key_get_id(payload, &tmp_len);
+  id = silc_id_payload_parse_id(tmp, tmp_len);
   if (!id)
     goto out;
 
   /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list->channels, id);
+  channel = silc_idlist_find_channel_by_id(server->local_list, id);
   if (!channel) {
     SILC_LOG_ERROR(("Received key for non-existent channel"));
-    SILC_LOG_DEBUG(("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)
+  tmp = silc_channel_key_get_key(payload, &tmp_len);
+  if (!tmp)
     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);
+  if (!silc_cipher_alloc(cipher, &channel->channel_key))
+    goto out;
+
+  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, 
-                                       key, key_len);
+                                       tmp, tmp_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;
+  /* XXX Some other sender should be used, I think this is not correct */
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    client = chl->client;
 
     if (client)
       silc_server_packet_send_dest(server, client->connection,
@@ -3439,10 +2680,31 @@ void silc_server_channel_key(SilcServer server,
   if (id)
     silc_free(id);
   if (payload)
-    silc_channel_key_free_payload(payload);
+    silc_channel_key_payload_free(payload);
   silc_buffer_free(buffer);
 }
 
+/* Sends current motd to client */
+
+void silc_server_send_motd(SilcServer server,
+                          SilcSocketConnection sock)
+{
+  char *motd;
+  int motd_len;
+
+  if (server->config && server->config->motd && 
+      server->config->motd->motd_file) {
+
+    motd = silc_file_read(server->config->motd->motd_file, &motd_len);
+    if (!motd)
+      return;
+
+    silc_server_send_notify(server, sock, SILC_NOTIFY_TYPE_MOTD, 1,
+                           motd, motd_len);
+    silc_free(motd);
+  }
+}
+
 /* Sends error message. Error messages may or may not have any 
    implications. */
 
@@ -3462,22 +2724,25 @@ void silc_server_send_error(SilcServer server,
                          buf, strlen(buf), FALSE);
 }
 
-/* Sends notify message */
+/* Sends notify message. If format is TRUE the variable arguments are
+   formatted and the formatted string is sent as argument payload. If it is
+   FALSE then each argument is sent as separate argument and their format
+   in the argument list must be { argument data, argument length }. */
 
 void silc_server_send_notify(SilcServer server,
                             SilcSocketConnection sock,
-                            const char *fmt, ...)
+                            SilcNotifyType type,
+                            unsigned int argc, ...)
 {
   va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer packet;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  va_start(ap, argc);
 
+  packet = silc_notify_payload_encode(type, argc, ap);
   silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, 
-                         buf, strlen(buf), FALSE);
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
 }
 
 /* Sends notify message destined to specific entity. */
@@ -3486,41 +2751,177 @@ void silc_server_send_notify_dest(SilcServer server,
                                  SilcSocketConnection sock,
                                  void *dest_id,
                                  SilcIdType dest_id_type,
-                                 const char *fmt, ...)
+                                 SilcNotifyType type,
+                                 unsigned int argc, ...)
 {
   va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer packet;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  va_start(ap, argc);
 
+  packet = silc_notify_payload_encode(type, argc, ap);
   silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 0, 
                               dest_id, dest_id_type,
-                              buf, strlen(buf), FALSE);
+                              packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
 }
 
 /* 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. */
+   distributed to all clients on the channel. */
 
 void silc_server_send_notify_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
-                                       const char *fmt, ...)
+                                       SilcChannelEntry channel,
+                                       SilcNotifyType type,
+                                       unsigned int argc, ...)
 {
   va_list ap;
-  unsigned char buf[4096];
+  SilcBuffer packet;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  va_start(ap, argc);
 
-  silc_server_packet_send_to_channel(server, channel, buf, 
-                                    strlen(buf), FALSE);
+  packet = silc_notify_payload_encode(type, argc, ap);
+  silc_server_packet_send_to_channel(server, channel, 
+                                    SILC_PACKET_NOTIFY,
+                                    packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+}
+
+/* Send notify message to all clients the client has joined. It is quaranteed
+   that the message is sent only once to a client (ie. if a client is joined
+   on two same channel it will receive only one notify message). Also, this
+   sends only to local clients (locally connected if we are server, and to
+   local servers if we are router). */
+
+void silc_server_send_notify_on_channels(SilcServer server,
+                                        SilcClientEntry client,
+                                        SilcNotifyType type,
+                                        unsigned int argc, ...)
+{
+  int i, j, k;
+  SilcSocketConnection sock = NULL;
+  SilcPacketContext packetdata;
+  SilcClientEntry c;
+  SilcClientEntry *sent_clients = NULL;
+  unsigned int sent_clients_count = 0;
+  SilcServerEntry *routed = NULL;
+  unsigned int routed_count = 0;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcCipher cipher;
+  SilcHmac hmac;
+  SilcBuffer packet;
+  unsigned char *data;
+  unsigned int data_len;
+  int force_send = FALSE;
+  va_list ap;
+
+  if (!client->channel_count)
+    return;
+
+  va_start(ap, argc);
+  packet = silc_notify_payload_encode(type, argc, ap);
+  data = packet->data;
+  data_len = packet->len;
+
+  /* Set the packet context pointers. */
+  packetdata.flags = 0;
+  packetdata.type = SILC_PACKET_NOTIFY;
+  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
+  packetdata.src_id_len = SILC_ID_SERVER_LEN;
+  packetdata.src_id_type = SILC_ID_SERVER;
+  packetdata.rng = server->rng;
+
+  for (i = 0; i < client->channel_count; i++) {
+    channel = client->channel[i];
+
+    /* Send the message to clients on the channel's client list. */
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+      c = chl->client;
+      
+      /* Check if we have sent the packet to this client already */
+      for (k = 0; k < sent_clients_count; k++)
+       if (sent_clients[k] == c)
+         break;
+      if (k < sent_clients_count)
+       continue;
+
+      /* If we are router and if this client has router set it is not
+        locally connected client and we will route the message to the
+        router set in the client. */
+      if (c && c->router && server->server_type == SILC_ROUTER) {
+       /* Check if we have sent the packet to this route already */
+       for (k = 0; k < routed_count; k++)
+         if (routed[k] == c->router)
+           break;
+       if (k < routed_count)
+         continue;
+       
+       /* Get data used in packet header encryption, keys and stuff. */
+       sock = (SilcSocketConnection)c->router->connection;
+       cipher = c->router->send_key;
+       hmac = c->router->hmac;
+       
+       packetdata.dst_id = silc_id_id2str(c->router->id, SILC_ID_SERVER);
+       packetdata.dst_id_len = SILC_ID_SERVER_LEN;
+       packetdata.dst_id_type = SILC_ID_SERVER;
+       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+         packetdata.src_id_len + packetdata.dst_id_len;
+       packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+
+       /* Send the packet */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               cipher, hmac, data,
+                                               data_len, FALSE, force_send);
+       
+       silc_free(packetdata.dst_id);
+
+       /* We want to make sure that the packet is routed to same router
+          only once. Mark this route as sent route. */
+       k = routed_count;
+       routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
+       routed[k] = c->router;
+       routed_count++;
+
+       continue;
+      }
+
+      /* Send to locally connected client */
+      if (c) {
+       
+       /* Get data used in packet header encryption, keys and stuff. */
+       sock = (SilcSocketConnection)c->connection;
+       cipher = c->send_key;
+       hmac = c->hmac;
+       
+       packetdata.dst_id = silc_id_id2str(c->id, SILC_ID_CLIENT);
+       packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
+       packetdata.dst_id_type = SILC_ID_CLIENT;
+       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+         packetdata.src_id_len + packetdata.dst_id_len;
+       packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+
+       /* Send the packet */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               cipher, hmac, data,
+                                               data_len, FALSE, force_send);
+
+       silc_free(packetdata.dst_id);
+
+       /* Make sure that we send the notify only once per client. */
+       sent_clients = silc_realloc(sent_clients, sizeof(*sent_clients) * 
+                                   (sent_clients_count + 1));
+       sent_clients[sent_clients_count] = c;
+       sent_clients_count++;
+      }
+    }
+  }
+
+  if (routed_count)
+    silc_free(routed);
+  if (sent_clients_count)
+    silc_free(sent_clients);
+  silc_free(packetdata.src_id);
 }
 
 /* Sends New ID Payload to remote end. The packet is used to distribute
@@ -3535,26 +2936,13 @@ void silc_server_send_new_id(SilcServer server,
                             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);
+  SilcBuffer idp;
 
+  idp = silc_id_payload_encode(id, id_type);
   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);
+                         idp->data, idp->len, FALSE);
+  silc_buffer_free(idp);
 }
 
 /* Sends Replace ID payload to remote end. This is used to replace old
@@ -3604,24 +2992,135 @@ void silc_server_send_replace_id(SilcServer server,
   silc_buffer_free(packet);
 }
 
+/* This function is used to send Remove Channel User payload. This may sent
+   by server but is usually used only by router to notify other routers that
+   user has left a channel. Normal server sends this packet to its router
+   to notify that the router should not hold a record about this client
+   on a channel anymore. Router distributes it further to other routers. */
+
+void silc_server_send_remove_channel_user(SilcServer server,
+                                         SilcSocketConnection sock,
+                                         int broadcast,
+                                         void *client_id, void *channel_id)
+{
+  SilcBuffer packet;
+  unsigned char *clid, *chid;
+
+  clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
+  if (!clid)
+    return;
+
+  chid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
+  if (!chid)
+    return;
+
+  packet = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN + SILC_ID_CHANNEL_LEN);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+                    SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
+                    SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                    SILC_STR_UI_XNSTRING(chid, SILC_ID_CHANNEL_LEN),
+                    SILC_STR_END);
+
+  silc_server_packet_send(server, sock, SILC_PACKET_REMOVE_CHANNEL_USER, 
+                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
+                         packet->data, packet->len, FALSE);
+  silc_free(clid);
+  silc_free(chid);
+  silc_buffer_free(packet);
+}
+
+/* Received packet to replace a ID. This checks that the requested ID
+   exists and replaces it with the new one. */
+
+void silc_server_replace_id(SilcServer server,
+                           SilcSocketConnection sock,
+                           SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  unsigned char *old_id = NULL, *new_id = NULL;
+  SilcIdType old_id_type, new_id_type;
+  unsigned short old_id_len, new_id_len;
+  void *id = NULL, *id2 = NULL;
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type == SILC_ID_CLIENT)
+    return;
+
+  SILC_LOG_DEBUG(("Replacing ID"));
+
+  silc_buffer_unformat(buffer,
+                      SILC_STR_UI_SHORT(&old_id_type),
+                      SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len),
+                      SILC_STR_UI_SHORT(&new_id_type),
+                      SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len),
+                      SILC_STR_END);
+
+  if (old_id_type != new_id_type)
+    goto out;
+
+  if (old_id_len != silc_id_get_len(old_id_type) ||
+      new_id_len != silc_id_get_len(new_id_type))
+    goto out;
+
+  id = silc_id_str2id(old_id, old_id_type);
+  if (!id)
+    goto out;
+
+  id2 = silc_id_str2id(new_id, new_id_type);
+  if (!id2)
+    goto out;
+
+  /* Replace the old ID */
+  switch(old_id_type) {
+  case SILC_ID_CLIENT:
+    if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
+      if (server->server_type == SILC_ROUTER)
+       silc_idlist_replace_client_id(server->global_list, id, id2);
+    break;
+
+  case SILC_ID_SERVER:
+    if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
+      if (server->server_type == SILC_ROUTER)
+       silc_idlist_replace_server_id(server->global_list, id, id2);
+    break;
+
+  case SILC_ID_CHANNEL:
+    /* XXX Hmm... Basically this cannot occur. Channel ID's cannot be
+       re-generated. */
+    silc_free(id2);
+    break;
+
+  default:
+    silc_free(id2);
+    break;
+  }
+
+ out:
+  if (id)
+    silc_free(id);
+  if (old_id)
+    silc_free(old_id);
+  if (new_id)
+    silc_free(new_id);
+}
+
 /* Creates new channel. */
 
-SilcChannelList *silc_server_new_channel(SilcServer server, 
+SilcChannelEntry silc_server_new_channel(SilcServer server, 
                                         SilcServerID *router_id,
                                         char *cipher, char *channel_name)
 {
   int i, channel_len, key_len;
   SilcChannelID *channel_id;
-  SilcChannelList *entry;
+  SilcChannelEntry entry;
   SilcCipher key;
   unsigned char channel_key[32], *id_string;
   SilcBuffer packet;
 
   SILC_LOG_DEBUG(("Creating new channel"));
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
-
   /* Create channel key */
   for (i = 0; i < 32; i++)
     channel_key[i] = silc_rng_get_byte(server->rng);
@@ -3634,15 +3133,24 @@ SilcChannelList *silc_server_new_channel(SilcServer server,
   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);
-  silc_idlist_add_channel(&server->local_list->channels, channel_name, 
-                         SILC_CHANNEL_MODE_NONE, channel_id, NULL, /*XXX*/
-                         key, &entry);
-  LCCC(channel_name[0]) = silc_idcache_add(&LCC(channel_name[0]), 
-                                          LCCC(channel_name[0]),
-                                          channel_name, SILC_ID_CHANNEL, 
-                                          channel_id, (void *)entry);
+  entry = silc_idlist_add_channel(server->local_list, channel_name, 
+                                 SILC_CHANNEL_MODE_NONE, channel_id, 
+                                 NULL, key);
+  if (!entry) {
+    silc_free(channel_name);
+    return NULL;
+  }
+
+#if 0
+  /* Add to cache */
+  silc_idcache_add(server->local_list->channels, channel_name,
+                  SILC_ID_CHANNEL, channel_id, (void *)entry, TRUE);
+#endif
+
   entry->key = silc_calloc(key_len, sizeof(*entry->key));
   entry->key_len = key_len * 8;
   memcpy(entry->key, channel_key, key_len);
@@ -3673,59 +3181,71 @@ SilcChannelList *silc_server_new_channel(SilcServer server,
     silc_buffer_free(packet);
   }
 
-#undef LCC
-#undef LCCC
   return entry;
 }
 
 /* Create new client. This processes incoming NEW_CLIENT packet and creates
-   Client ID for the client and adds it to lists and cache. */
+   Client ID for the client. Client becomes registered after calling this
+   functions. */
 
-SilcClientList *silc_server_new_client(SilcServer server,
+SilcClientEntry silc_server_new_client(SilcServer server,
                                       SilcSocketConnection sock,
                                       SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcClientList *id_entry;
-  char *username = NULL, *realname = NULL, *id_string;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache;
+  SilcClientID *client_id;
   SilcBuffer reply;
+  char *username = NULL, *realname = NULL, *id_string;
 
   SILC_LOG_DEBUG(("Creating new client"));
 
   if (sock->type != SILC_SOCKET_TYPE_CLIENT)
     return NULL;
 
-#define LCC(x) server->local_list->client_cache[(x) - 32]
-#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+  /* Take client entry */
+  client = (SilcClientEntry)sock->user_data;
+
+  /* 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);
 
-  /* Set the pointers to the client list and create new client ID */
-  id_entry = (SilcClientList *)sock->user_data;
-  id_entry->nickname = strdup(username);
-  id_entry->username = username;
-  id_entry->userinfo = realname;
+  /* Create Client ID */
   silc_id_create_client_id(server->id, server->rng, server->md5hash,
-                          username, &id_entry->id);
+                          username, &client_id);
 
-  /* Add to client cache */
-  LCCC(username[0]) = silc_idcache_add(&LCC(username[0]), 
-                                      LCCC(username[0]),
-                                      username, SILC_ID_CLIENT, 
-                                      id_entry->id, (void *)id_entry);
+  /* 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,
-                           id_entry->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+                           client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
   
   /* Send the new client ID to the client. */
-  id_string = silc_id_id2str(id_entry->id, SILC_ID_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,
@@ -3737,30 +3257,28 @@ SilcClientList *silc_server_new_client(SilcServer server,
                          reply->data, reply->len, FALSE);
   silc_free(id_string);
   silc_buffer_free(reply);
-  
-  /* Send some nice info to the client */
-  silc_server_send_notify(server, sock, 
-                         "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",
-                         id_entry->send_key->cipher->name,
-                         id_entry->send_key->cipher->key_len);
-  silc_server_send_notify(server, sock, 
-                         "Your current nickname is %s",
-                         id_entry->nickname);
-
-  /* XXX Send motd */
 
-#undef LCC
-#undef LCCC
-  return id_entry;
+  /* Send some nice info to the client */
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Welcome to the SILC Network %s@%s",
+                          username, sock->hostname));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your host is %s, running version %s",
+                          server->config->server_info->server_name,
+                          server_version));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your connection is secured with %s cipher, "
+                          "key length %d bits",
+                          client->send_key->cipher->name,
+                          client->send_key->cipher->key_len));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your current nickname is %s",
+                          client->nickname));
+
+  /* Send motd */
+  silc_server_send_motd(server, sock);
+
+  return client;
 }
 
 /* Create new server. This processes incoming NEW_SERVER packet and
@@ -3768,16 +3286,19 @@ SilcClientList *silc_server_new_client(SilcServer server,
    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
+   to the server. If router uses this it means that one of the cell's
    servers is connected to the router. */
 
-SilcServerList *silc_server_new_server(SilcServer server,
+SilcServerEntry silc_server_new_server(SilcServer server,
                                       SilcSocketConnection sock,
                                       SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcServerList *id_entry;
+  SilcServerEntry new_server;
+  SilcIDCacheEntry cache;
+  SilcServerID *server_id;
   unsigned char *server_name, *id_string;
+  unsigned short id_len;
 
   SILC_LOG_DEBUG(("Creating new server"));
 
@@ -3785,39 +3306,52 @@ SilcServerList *silc_server_new_server(SilcServer server,
       sock->type != SILC_SOCKET_TYPE_ROUTER)
     return NULL;
 
-#define LSC(x) server->local_list->server_cache[(x) - 32]
-#define LSCC(x) server->local_list->server_cache_count[(x) - 32]
+  /* Take server entry */
+  new_server = (SilcServerEntry)sock->user_data;
 
+  /* 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;
+  }
+
+  /* Parse the incoming packet */
   silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
+                      SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
                       SILC_STR_UI16_STRING_ALLOC(&server_name),
                       SILC_STR_END);
 
-  /* Save ID and name */
-  id_entry = (SilcServerList *)sock->user_data;
-  id_entry->id = silc_id_str2id(id_string, SILC_ID_SERVER);
-  id_entry->server_name = server_name;
-  
-  /* Add to server cache */
-  LSCC(server_name[0]) = 
-    silc_idcache_add(&LSC(server_name[0]), 
-                    LSCC(server_name[0]),
-                    server_name, SILC_ID_SERVER, 
-                    id_entry->id, (void *)id_entry);
+  if (id_len > buffer->len) {
+    silc_free(id_string);
+    silc_free(server_name);
+    return NULL;
+  }
+
+  /* Get Server ID */
+  server_id = silc_id_payload_parse_id(id_string, id_len);
+  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, id_entry->id, SILC_ID_SERVER, 
+                           TRUE, new_server->id, SILC_ID_SERVER, 
                            SILC_ID_SERVER_LEN);
 
-  silc_free(id_string);
-
-#undef LSC
-#undef LSCC
-  return id_entry;
+  return new_server;
 }
 
 /* Processes incoming New ID Payload. New ID Payload is used to distribute
@@ -3828,74 +3362,148 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
                        SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
+  SilcIDList id_list;
+  SilcServerEntry tmpserver, router;
+  SilcSocketConnection router_sock;
+  SilcIDPayload idp;
   SilcIdType id_type;
-  unsigned char *id_string;
-  void *id;
+  void *id, *tmpid;
 
   SILC_LOG_DEBUG(("Processing new ID"));
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
+      server->server_type == SILC_SERVER ||
+      packet->src_id_type != SILC_ID_SERVER)
     return;
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&id_type),
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_END);
+  idp = silc_id_payload_parse(buffer);
+  if (!idp)
+    return;
+
+  id_type = silc_id_payload_get_type(idp);
 
   /* Normal server cannot have other normal server connections */
   if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
     goto out;
 
-  id = silc_id_str2id(id_string, id_type);
+  id = silc_id_payload_get_id(idp);
   if (!id)
     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 */
+  /* If the packet is originated from the one who sent it to us we know
+     that the ID belongs to our cell, unless the sender was router. */
+  tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
+  tmpserver = (SilcServerEntry)sock->user_data;
+
+  if (!SILC_ID_SERVER_COMPARE(tmpid, tmpserver->id) &&
+      sock->type == SILC_SOCKET_TYPE_SERVER) {
+    id_list = server->local_list;
+    router_sock = sock;
+    router = sock->user_data;
+    /*    router = server->id_entry; */
+  } else {
+    id_list = server->global_list;
+    router_sock = (SilcSocketConnection)server->id_entry->router->connection;
+    router = server->id_entry->router;
+  }
+
+  silc_free(tmpid);
 
   switch(id_type) {
   case SILC_ID_CLIENT:
     {
-      SilcClientList *idlist;
+      SilcClientEntry idlist;
 
       /* Add the client to our local list. We are router and we keep
         cell specific local database of all clients in the cell. */
-      silc_idlist_add_client(&server->local_list->clients, NULL, NULL, NULL,
-                            id, sock->user_data, NULL, NULL, 
-                            NULL, NULL, &idlist);
-      idlist->connection = sock;
+      idlist = silc_idlist_add_client(id_list, NULL, NULL, NULL,
+                                     id, router, NULL, NULL, 
+                                     NULL, NULL, NULL, router_sock);
     }
     break;
 
   case SILC_ID_SERVER:
     {
-      SilcServerList *idlist;
+      SilcServerEntry idlist;
 
       /* Add the server to our local list. We are router and we keep
         cell specific local database of all servers in the cell. */
-      silc_idlist_add_server(&server->local_list->servers, NULL, 0,
-                            id, server->id_entry, NULL, NULL, 
-                          NULL, NULL, &idlist);
-      idlist->connection = sock;
+      idlist = silc_idlist_add_server(id_list, NULL, 0,
+                                     id, router, NULL, NULL, 
+                                     NULL, NULL, NULL, router_sock);
     }
     break;
 
   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->channels, NULL, 0,
-                           id, server->id_entry, NULL, NULL);
+    silc_idlist_add_channel(id_list, NULL, 0, id, router, NULL);
     break;
 
   default:
-    goto out;
     break;
   }
 
  out:
-  silc_free(id_string);
+  silc_id_payload_free(idp);
+}
+
+/* Received packet to remove a user from a channel. Routers notify other
+   routers that user has left a channel. Client must not send this packet. 
+   Normal server may send this packet but ignores if it receives one. */
+
+void silc_server_remove_channel_user(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  unsigned char *tmp1 = NULL, *tmp2 = NULL;
+  SilcClientID *client_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcChannelEntry channel;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Removing user from channel"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      server->server_type == SILC_SERVER)
+    return;
+
+  silc_buffer_unformat(buffer,
+                      SILC_STR_UI16_STRING_ALLOC(&tmp1),
+                      SILC_STR_UI16_STRING_ALLOC(&tmp2),
+                      SILC_STR_END);
+
+  if (!tmp1 || !tmp2)
+    goto out;
+
+  client_id = silc_id_str2id(tmp1, SILC_ID_CLIENT);
+  channel_id = silc_id_str2id(tmp2, SILC_ID_CHANNEL);
+  if (!client_id || !channel_id)
+    goto out;
+
+  /* XXX routers should check server->global_list as well */
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+  if (!channel)
+    goto out;
+  
+  /* XXX routers should check server->global_list as well */
+  /* Get client entry */
+  client = silc_idlist_find_client_by_id(server->local_list, client_id);
+  if (!client)
+    goto out;
+
+  /* Remove from channel */
+  silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+
+ out:
+  if (tmp1)
+    silc_free(tmp1);
+  if (tmp2)
+    silc_free(tmp2);
+  if (client_id)
+    silc_free(client_id);
+  if (channel_id)
+    silc_free(channel_id);
 }