updates.
[silc.git] / apps / silcd / packet_receive.c
index 83e19b8ef7ef4d8949adcc009ca129b43ea7ce65..46b0aac16c8e3da3184c30bb2a3ec7e12f157f74 100644 (file)
 
 extern char *server_version;
 
-/* Check whereto relay the received notify packet that was destined
-   to a client. */
-
-static void 
-silc_server_packet_process_relay_notify(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       SilcPacketContext *packet)
-{
-  SilcClientID *id;
-  SilcServerEntry router;
-  SilcSocketConnection dst_sock;
-  SilcClientEntry client;
-  SilcIDListData idata;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
-  if (!id) {
-    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
-    return;
-  }
-
-  /* If the destination belongs to our server we don't have to route
-     the packet anywhere but to send it to the local destination. */
-  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
-  if (client) {
-    /* It exists, now deliver the packet to the destination */
-    dst_sock = (SilcSocketConnection)client->connection;
-
-    /* If we are router and the client has router then the client is in
-       our cell but not directly connected to us. */
-    if (server->server_type == SILC_ROUTER && client->router) {
-      /* 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)client->router;
-      idata = (SilcIDListData)router;
-      silc_server_packet_relay_notify(server, router->connection,
-                                     idata->send_key,
-                                     idata->hmac,
-                                     packet);
-      silc_free(id);
-      return;
-    }
-
-    /* Seems that client really is directly connected to us */
-    idata = (SilcIDListData)client;
-    silc_server_packet_relay_notify(server, dst_sock, 
-                                   idata->send_key,
-                                   idata->hmac, packet);
-    silc_free(id);
-    return;
-  }
-
-  /* Destination belongs to someone not in this server. If we are normal
-     server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
-    router = server->router;
-
-    /* Send to primary route */
-    if (router) {
-      dst_sock = (SilcSocketConnection)router->connection;
-      idata = (SilcIDListData)router;
-      silc_server_packet_relay_notify(server, dst_sock, 
-                                     idata->send_key,
-                                     idata->hmac, packet);
-    }
-    silc_free(id);
-    return;
-  }
-
-  /* We are router and we will perform route lookup for the destination 
-     and send the packet to fastest route. */
-  if (server->server_type == SILC_ROUTER && !server->standalone) {
-    /* Check first that the ID is valid */
-    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
-    if (client) {
-      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
-      router = (SilcServerEntry)dst_sock->user_data;
-      idata = (SilcIDListData)router;
-
-      /* Get fastest route and send packet. */
-      if (router)
-       silc_server_packet_relay_notify(server, dst_sock, 
-                                       idata->send_key,
-                                       idata->hmac, packet);
-      silc_free(id);
-      return;
-    }
-  }
-}
-
 /* Received notify packet. Server can receive notify packets from router. 
    Server then relays the notify messages to clients if needed. */
 
@@ -130,14 +37,17 @@ void silc_server_notify(SilcServer server,
   SilcNotifyPayload payload;
   SilcNotifyType type;
   SilcArgumentPayload args;
-  SilcChannelID *channel_id;
+  SilcChannelID *channel_id, *channel_id2;
   SilcClientID *client_id, *client_id2;
+  SilcServerID *server_id;
   SilcChannelEntry channel;
   SilcClientEntry client;
+  SilcServerEntry server_entry;
   SilcChannelClientEntry chl;
-  unsigned int mode;
+  SilcIDCacheEntry cache;
+  uint32 mode;
   unsigned char *tmp;
-  unsigned int tmp_len;
+  uint32 tmp_len;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -145,11 +55,22 @@ void silc_server_notify(SilcServer server,
       packet->src_id_type != SILC_ID_SERVER)
     return;
 
-  /* If the packet is destined directly to a client, then we don't
-     process the packet at all but just relay it to the client. */
-  if (packet->dst_id_type == SILC_ID_CLIENT) {
-    silc_server_packet_process_relay_notify(server, sock, packet);
+  if (!packet->dst_id)
     return;
+
+  /* If the packet is destined directly to a client then relay the packet
+     before processing it. */
+  if (packet->dst_id_type == SILC_ID_CLIENT) {
+    SilcIDListData idata;
+    SilcSocketConnection dst_sock;
+
+    /* Get the route to the client */
+    dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                           packet->dst_id_len, NULL, &idata);
+    if (dst_sock)
+      /* Relay the packet */
+      silc_server_relay_packet(server, dst_sock, idata->send_key,
+                              idata->hmac, packet, TRUE);
   }
 
   /* If we are router and this packet is not already broadcast packet
@@ -190,10 +111,10 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
        silc_free(channel_id);
@@ -218,10 +139,10 @@ void silc_server_notify(SilcServer server,
     /* If the the client is not in local list we check global list (ie. the
        channel will be global channel) and if it does not exist then create
        entry for the client. */
-    client = silc_idlist_find_client_by_id(server->local_list, 
+    client = silc_idlist_find_client_by_id(server->global_list, 
                                           client_id, NULL);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->global_list, 
+      client = silc_idlist_find_client_by_id(server->local_list, 
                                             client_id, NULL);
       if (!client) {
        /* If router did not find the client the it is bogus */
@@ -229,13 +150,15 @@ void silc_server_notify(SilcServer server,
          goto out;
 
        client = 
-         silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+         silc_idlist_add_client(server->global_list, NULL, 0, NULL, NULL,
                                 silc_id_dup(client_id, SILC_ID_CLIENT), 
                                 sock->user_data, NULL);
        if (!client) {
          silc_free(client_id);
          goto out;
        }
+
+       client->data.registered = TRUE;
       }
     }
 
@@ -271,10 +194,10 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
                                             channel_id, NULL);
     if (!channel) { 
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
        silc_free(channel_id);
@@ -333,10 +256,10 @@ void silc_server_notify(SilcServer server,
 
     /* Get client entry */
     client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, NULL);
+                                          client_id, &cache);
     if (!client) {
       client = silc_idlist_find_client_by_id(server->local_list, 
-                                            client_id, NULL);
+                                            client_id, &cache);
       if (!client) {
        silc_free(client_id);
        goto out;
@@ -352,9 +275,14 @@ void silc_server_notify(SilcServer server,
     /* Remove the client from all channels */
     silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, TRUE);
 
+    client->data.registered = FALSE;
+    cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+
+#if 0
     /* Remove the client entry */
     if (!silc_idlist_del_client(server->global_list, client))
       silc_idlist_del_client(server->local_list, client);
+#endif
     break;
 
   case SILC_NOTIFY_TYPE_TOPIC_SET:
@@ -370,10 +298,10 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
        silc_free(channel_id);
@@ -471,10 +399,10 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
        silc_free(channel_id);
@@ -496,9 +424,43 @@ void silc_server_notify(SilcServer server,
 
     SILC_GET32_MSB(mode, tmp);
 
+    /* If the channel had private keys set and the mode was removed then
+       we must re-generate and re-distribute a new channel key */
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
+       !(mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, 0);
+      
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
+
     /* Change mode */
     channel->mode = mode;
     silc_free(channel_id);
+
+    /* Get the hmac */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp) {
+      unsigned char hash[32];
+
+      if (channel->hmac)
+       silc_hmac_free(channel->hmac);
+      if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
+       goto out;
+
+      /* Set the HMAC key out of current channel key. The client must do
+        this locally. */
+      silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8, 
+                    hash);
+      silc_hmac_set_key(channel->hmac, hash, 
+                       silc_hash_len(channel->hmac->hash));
+      memset(hash, 0, sizeof(hash));
+    }
+
     break;
 
   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
@@ -514,10 +476,10 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
        silc_free(channel_id);
@@ -572,15 +534,191 @@ void silc_server_notify(SilcServer server,
     break;
 
   case SILC_NOTIFY_TYPE_INVITE:
-    SILC_LOG_DEBUG(("INVITE notify (not-impl XXX)"));
+
+    if (packet->dst_id_type == SILC_ID_CLIENT)
+      goto out;
+
+    SILC_LOG_DEBUG(("INVITE notify"));
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
+
+    /* Get the added invite */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (tmp) {
+      if (!channel->invite_list)
+       channel->invite_list = silc_calloc(tmp_len + 2, 
+                                          sizeof(*channel->invite_list));
+      else
+       channel->invite_list = silc_realloc(channel->invite_list, 
+                                           sizeof(*channel->invite_list) * 
+                                           (tmp_len + 
+                                            strlen(channel->invite_list) + 
+                                            2));
+      if (tmp[tmp_len - 1] == ',')
+       tmp[tmp_len - 1] = '\0';
+      
+      strncat(channel->invite_list, tmp, tmp_len);
+      strncat(channel->invite_list, ",", 1);
+    }
+
+    /* Get the deleted invite */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp && channel->invite_list) {
+      char *start, *end, *n;
+      
+      if (!strncmp(channel->invite_list, tmp, 
+                  strlen(channel->invite_list) - 1)) {
+       silc_free(channel->invite_list);
+       channel->invite_list = NULL;
+      } else {
+       start = strstr(channel->invite_list, tmp);
+       if (start && strlen(start) >= tmp_len) {
+         end = start + tmp_len;
+         n = silc_calloc(strlen(channel->invite_list) - tmp_len, sizeof(*n));
+         strncat(n, channel->invite_list, start - channel->invite_list);
+         strncat(n, end + 1, ((channel->invite_list + 
+                               strlen(channel->invite_list)) - end) - 1);
+         silc_free(channel->invite_list);
+         channel->invite_list = n;
+       }
+      }
+    }
+
     break;
 
   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
-    SILC_LOG_DEBUG(("CHANNEL CHANGE notify (not-impl XXX)"));
+    /*
+     * Distribute to the local clients on the channel and change the
+     * channel ID.
+     */
+
+    SILC_LOG_DEBUG(("CHANNEL CHANGE"));
+
+    if (sock->type != SILC_SOCKET_TYPE_ROUTER)
+      break;
+
+    /* Get the old Channel ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get the channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+
+    /* Send the notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    /* Get the new Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id2 = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id2)
+      goto out;
+
+    SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
+                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
+    SILC_LOG_DEBUG(("New Channel ID id(%s)", 
+                   silc_id_render(channel_id2, SILC_ID_CHANNEL)));
+
+    /* Replace the Channel ID */
+    if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
+                                       channel_id2))
+      if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+                                         channel_id2)) {
+       silc_free(channel_id2);
+       channel_id2 = NULL;
+      }
+
+    if (channel_id2) {
+      SilcBuffer users = NULL;
+      
+      /* Re-announce our clients on the channel as the ID has changed now */
+      silc_server_announce_get_channel_users(server, channel, &users);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
+    }
+
+    silc_free(channel_id);
+
     break;
 
   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
-    SILC_LOG_DEBUG(("SERVER SIGNOFF notify (not-impl XXX)"));
+    /* 
+     * Remove the server entry and all clients that this server owns.
+     */
+
+    SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
+
+    /* Get Server ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    server_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!server_id)
+      goto out;
+
+    /* Get server entry */
+    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                server_id, NULL);
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                  server_id, NULL);
+      if (!server_entry) {
+       silc_free(server_id);
+       goto out;
+      }
+    }
+    silc_free(server_id);
+
+    /* Free all client entries that this server owns as they will
+       become invalid now as well. */
+    silc_server_remove_clients_by_server(server, server_entry, TRUE);
+
+    /* Remove the server entry */
+    if (!silc_idlist_del_server(server->global_list, server_entry))
+      silc_idlist_del_server(server->local_list, server_entry);
+
+    /* XXX update statistics */
+
     break;
 
   case SILC_NOTIFY_TYPE_KICKED:
@@ -596,10 +734,10 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
        silc_free(channel_id);
@@ -622,10 +760,10 @@ void silc_server_notify(SilcServer server,
                                       packet->buffer->len, FALSE);
 
     /* If the the client is not in local list we check global list */
-    client = silc_idlist_find_client_by_id(server->local_list, 
+    client = silc_idlist_find_client_by_id(server->global_list, 
                                           client_id, NULL);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->global_list, 
+      client = silc_idlist_find_client_by_id(server->local_list, 
                                             client_id, NULL);
       if (!client) {
        silc_free(client_id);
@@ -639,12 +777,169 @@ void silc_server_notify(SilcServer server,
     break;
 
   case SILC_NOTIFY_TYPE_KILLED:
-    /* 
-     * Distribute the notify to local clients on channels
+    {
+      /* 
+       * Distribute the notify to local clients on channels
+       */
+      unsigned char *id;
+      uint32 id_len;
+    
+      SILC_LOG_DEBUG(("KILLED notify"));
+      
+      /* Get client ID */
+      id = silc_argument_get_arg_type(args, 1, &id_len);
+      if (!id)
+       goto out;
+      client_id = silc_id_payload_parse_id(id, id_len);
+      if (!client_id)
+       goto out;
+
+      /* If the the client is not in local list we check global list */
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, NULL);
+      if (!client) {
+       client = silc_idlist_find_client_by_id(server->local_list, 
+                                              client_id, NULL);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+      silc_free(client_id);
+
+      /* If the client is one of ours, then close the connection to the
+        client now. This removes the client from all channels as well. */
+      if (packet->dst_id_type == SILC_ID_CLIENT && client->data.registered &&
+         client->connection) {
+       sock = client->connection;
+       silc_server_free_client_data(server, NULL, client, FALSE, NULL);
+       silc_server_close_connection(server, sock);
+       break;
+      }
+
+      /* Get comment */
+      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (tmp_len > 128)
+       tmp = NULL;
+
+      /* Send the notify to local clients on the channels except to the
+        client who is killed. */
+      silc_server_send_notify_on_channels(server, client, client,
+                                         SILC_NOTIFY_TYPE_KILLED, 
+                                         tmp ? 2 : 1,
+                                         id, id_len, 
+                                         tmp, tmp_len);
+
+      /* Remove the client from all channels */
+      silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
+                                      FALSE);
+
+      break;
+    }
+
+  case SILC_NOTIFY_TYPE_UMODE_CHANGE:
+    /*
+     * Save the mode of the client.
      */
+
+    SILC_LOG_DEBUG(("UMODE_CHANGE notify"));
+      
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, NULL);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    /* Save the mode */
+    SILC_GET32_MSB(client->mode, tmp);
+
+    break;
+
+  case SILC_NOTIFY_TYPE_BAN:
+    /*
+     * Save the ban
+     */
+
+    SILC_LOG_DEBUG(("BAN notify"));
+    
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
     
-    SILC_LOG_DEBUG(("KILLED notify"));
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
+
+    /* Get the new ban and add it to the ban list */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp) {
+      if (!channel->ban_list)
+       channel->ban_list = silc_calloc(tmp_len + 2, 
+                                       sizeof(*channel->ban_list));
+      else
+       channel->ban_list = silc_realloc(channel->ban_list, 
+                                        sizeof(*channel->ban_list) * 
+                                        (tmp_len + 
+                                         strlen(channel->ban_list) + 2));
+      strncat(channel->ban_list, tmp, tmp_len);
+      strncat(channel->ban_list, ",", 1);
+    }
+
+    /* Get the ban to be removed and remove it from the list */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (tmp && channel->ban_list) {
+      char *start, *end, *n;
       
+      if (!strcmp(channel->ban_list, tmp)) {
+       silc_free(channel->ban_list);
+       channel->ban_list = NULL;
+      } else {
+       start = strstr(channel->ban_list, tmp);
+       if (start && strlen(start) >= tmp_len) {
+         end = start + tmp_len;
+         n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
+         strncat(n, channel->ban_list, start - channel->ban_list);
+         strncat(n, end + 1, ((channel->ban_list + 
+                               strlen(channel->ban_list)) - end) - 1);
+         silc_free(channel->ban_list);
+         channel->ban_list = n;
+       }
+      }
+    }
+
     break;
 
     /* Ignore rest of the notify types for now */
@@ -665,7 +960,7 @@ void silc_server_notify_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  unsigned short len;
+  uint16 len;
 
   SILC_LOG_DEBUG(("Processing New Notify List"));
 
@@ -722,10 +1017,7 @@ void silc_server_private_message(SilcServer server,
                                 SilcSocketConnection sock,
                                 SilcPacketContext *packet)
 {
-  SilcClientID *id;
-  SilcServerEntry router;
   SilcSocketConnection dst_sock;
-  SilcClientEntry client;
   SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
@@ -737,82 +1029,15 @@ void silc_server_private_message(SilcServer server,
   if (!packet->dst_id)
     return;
 
-  /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
-  if (!id) {
-    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
-    return;
-  }
-
-  /* If the destination belongs to our server we don't have to route
-     the message anywhere but to send it to the local destination. */
-  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
-  if (client) {
-    /* It exists, now deliver the message to the destination */
-    dst_sock = (SilcSocketConnection)client->connection;
-
-    /* If we are router and the client has router then the client is in
-       our cell but not directly connected to us. */
-    if (server->server_type == SILC_ROUTER && client->router) {
-      /* 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)client->router;
-      idata = (SilcIDListData)router;
-
-      silc_server_send_private_message(server, router->connection,
-                                      idata->send_key,
-                                      idata->hmac,
-                                      packet);
-      silc_free(id);
-      return;
-    }
-
-    /* Seems that client really is directly connected to us */
-    idata = (SilcIDListData)client;
-    silc_server_send_private_message(server, dst_sock, 
-                                    idata->send_key,
-                                    idata->hmac, packet);
-    silc_free(id);
-    return;
-  }
-
-  /* Destination belongs to someone not in this server. If we are normal
-     server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
-    router = server->router;
-
-    /* Send to primary route */
-    if (router) {
-      dst_sock = (SilcSocketConnection)router->connection;
-      idata = (SilcIDListData)router;
-      silc_server_send_private_message(server, dst_sock, 
-                                      idata->send_key,
-                                      idata->hmac, packet);
-    }
-    silc_free(id);
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
     return;
-  }
 
-  /* 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) {
-    /* Check first that the ID is valid */
-    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
-    if (client) {
-      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
-      router = (SilcServerEntry)dst_sock->user_data;
-      idata = (SilcIDListData)router;
-
-      /* Get fastest route and send packet. */
-      if (router)
-       silc_server_send_private_message(server, dst_sock, 
-                                        idata->send_key,
-                                        idata->hmac, packet);
-      silc_free(id);
-      return;
-    }
-  }
+  /* Send the private message */
+  silc_server_send_private_message(server, dst_sock, idata->send_key,
+                                  idata->hmac, packet);
 }
 
 /* Received private message key packet.. This packet is never for us. It is to
@@ -824,10 +1049,7 @@ void silc_server_private_message_key(SilcServer server,
                                     SilcSocketConnection sock,
                                     SilcPacketContext *packet)
 {
-  SilcClientID *id;
-  SilcServerEntry router;
   SilcSocketConnection dst_sock;
-  SilcClientEntry client;
   SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
@@ -839,81 +1061,15 @@ void silc_server_private_message_key(SilcServer server,
   if (!packet->dst_id)
     return;
 
-  /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
-  if (!id) {
-    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
-    return;
-  }
-
-  /* If the destination belongs to our server we don't have to route
-     the message anywhere but to send it to the local destination. */
-  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
-  if (client) {
-    /* It exists, now deliver the message to the destination */
-    dst_sock = (SilcSocketConnection)client->connection;
-
-    /* If we are router and the client has router then the client is in
-       our cell but not directly connected to us. */
-    if (server->server_type == SILC_ROUTER && client->router) {
-      /* 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)client->router;
-      idata = (SilcIDListData)router;
-      silc_server_send_private_message_key(server, router->connection,
-                                          idata->send_key,
-                                          idata->hmac,
-                                          packet);
-      silc_free(id);
-      return;
-    }
-
-    /* Seems that client really is directly connected to us */
-    idata = (SilcIDListData)client;
-    silc_server_send_private_message_key(server, dst_sock, 
-                                        idata->send_key,
-                                        idata->hmac, packet);
-    silc_free(id);
-    return;
-  }
-
-  /* Destination belongs to someone not in this server. If we are normal
-     server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
-    router = server->router;
-
-    /* Send to primary route */
-    if (router) {
-      dst_sock = (SilcSocketConnection)router->connection;
-      idata = (SilcIDListData)router;
-      silc_server_send_private_message_key(server, dst_sock, 
-                                          idata->send_key,
-                                          idata->hmac, packet);
-    }
-    silc_free(id);
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
     return;
-  }
 
-  /* We are router and we will perform route lookup for the destination 
-     and send the packet to fastest route. */
-  if (server->server_type == SILC_ROUTER && !server->standalone) {
-    /* Check first that the ID is valid */
-    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
-    if (client) {
-      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
-      router = (SilcServerEntry)dst_sock->user_data;
-      idata = (SilcIDListData)router;
-
-      /* Get fastest route and send packet. */
-      if (router)
-       silc_server_send_private_message_key(server, dst_sock, 
-                                            idata->send_key,
-                                            idata->hmac, packet);
-      silc_free(id);
-      return;
-    }
-  }
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac, packet, FALSE);
 }
 
 /* Processes incoming command reply packet. The command reply packet may
@@ -1050,7 +1206,8 @@ void silc_server_channel_message(SilcServer server,
       sock->type == SILC_SOCKET_TYPE_ROUTER &&
       !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     SilcBuffer chp;
-    unsigned int iv_len, i, data_len;
+    uint32 iv_len, i;
+    uint16 data_len, flags;
 
     iv_len = silc_cipher_get_block_len(channel->channel_key);
     if (channel->iv[0] == '\0')
@@ -1060,11 +1217,13 @@ void silc_server_channel_message(SilcServer server,
       silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
     
     /* Encode new payload. This encrypts it also. */
-    SILC_GET16_MSB(data_len, packet->buffer->data);
-    chp = silc_channel_payload_encode(data_len, packet->buffer->data + 2,
-                                     iv_len, channel->iv,
-                                     channel->channel_key,
-                                     channel->hmac, server->rng);
+    SILC_GET16_MSB(flags, packet->buffer->data);
+    SILC_GET16_MSB(data_len, packet->buffer->data + 2);
+    chp = silc_channel_message_payload_encode(flags, data_len, 
+                                             packet->buffer->data + 4,
+                                             iv_len, channel->iv,
+                                             channel->channel_key,
+                                             channel->hmac);
     silc_buffer_put(packet->buffer, chp->data, chp->len);
     silc_buffer_free(chp);
   }
@@ -1093,7 +1252,9 @@ void silc_server_channel_key(SilcServer server,
   SilcBuffer buffer = packet->buffer;
   SilcChannelEntry channel;
 
-  if (packet->src_id_type != SILC_ID_SERVER)
+  if (packet->src_id_type != SILC_ID_SERVER ||
+      (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER))
     return;
 
   /* Save the channel key */
@@ -1178,6 +1339,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   cache->id = (void *)client_id;
   cache->type = SILC_ID_CLIENT;
   cache->data = username;
+  cache->data_len = strlen(username);
   silc_idcache_sort_by_data(server->local_list->clients);
 
   /* Notify our router about new client on the SILC network */
@@ -1272,7 +1434,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   SilcServerID *server_id;
   SilcIDListData idata;
   unsigned char *server_name, *id_string;
-  unsigned short id_len;
+  uint16 id_len, name_len;
   int ret;
 
   SILC_LOG_DEBUG(("Creating new server"));
@@ -1295,7 +1457,8 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   /* Parse the incoming packet */
   ret = silc_buffer_unformat(buffer,
                             SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
-                            SILC_STR_UI16_STRING_ALLOC(&server_name),
+                            SILC_STR_UI16_NSTRING_ALLOC(&server_name, 
+                                                        &name_len),
                             SILC_STR_END);
   if (ret == -1) {
     if (id_string)
@@ -1311,6 +1474,9 @@ SilcServerEntry silc_server_new_server(SilcServer server,
     return NULL;
   }
 
+  if (name_len > 256)
+    server_name[255] = '\0';
+
   /* Get Server ID */
   server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER);
   if (!server_id) {
@@ -1329,6 +1495,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   cache->id = (void *)server_id;
   cache->type = SILC_ID_SERVER;
   cache->data = server_name;
+  cache->data_len = strlen(server_name);
   silc_idcache_sort_by_data(server->local_list->servers);
 
   /* Distribute the information about new server in the SILC network
@@ -1419,31 +1586,19 @@ static void silc_server_new_id_real(SilcServer server,
         list. The client is put to global list and we will take the hash
         value of the Client ID and save it to the ID Cache system for fast
         searching in the future. */
-      hash = silc_calloc(sizeof(((SilcClientID *)id)->hash), 
+      hash = silc_calloc(sizeof(((SilcClientID *)id)->hash),
                         sizeof(unsigned char));
       memcpy(hash, ((SilcClientID *)id)->hash, 
             sizeof(((SilcClientID *)id)->hash));
-      entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id, 
-                                    router, NULL);
+      entry = silc_idlist_add_client(id_list, hash, 
+                                    sizeof(((SilcClientID *)id)->hash),
+                                    NULL, NULL, id, router, NULL);
       entry->nickname = NULL;
+      entry->data.registered = TRUE;
 
       if (sock->type == SILC_SOCKET_TYPE_SERVER)
        server->stat.cell_clients++;
       server->stat.clients++;
-
-#if 0
-      /* XXX Adding two ID's with same IP number replaces the old entry thus
-        gives wrong route. Thus, now disabled until figured out a better way
-        to do this or when removed the whole thing. This could be removed
-        because entry->router->connection gives always the most optimal route
-        for the ID anyway (unless new routes (faster perhaps) are established
-        after receiving this ID, this we don't know however). */
-      /* Add route cache for this ID */
-      silc_server_route_add(silc_server_route_hash(
-                           ((SilcClientID *)id)->ip.s_addr,
-                           server->id->port), ((SilcClientID *)id)->ip.s_addr,
-                           router);
-#endif
     }
     break;
 
@@ -1460,15 +1615,6 @@ static void silc_server_new_id_real(SilcServer server,
     if (sock->type == SILC_SOCKET_TYPE_SERVER)
       server->stat.cell_servers++;
     server->stat.servers++;
-
-#if 0
-    /* Add route cache for this ID */
-    silc_server_route_add(silc_server_route_hash(
-                         ((SilcServerID *)id)->ip.s_addr,
-                         ((SilcServerID *)id)->port), 
-                         ((SilcServerID *)id)->ip.s_addr,
-                         router);
-#endif
     break;
 
   case SILC_ID_CHANNEL:
@@ -1501,7 +1647,7 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
 {
   SilcPacketContext *new_id;
   SilcBuffer idp;
-  unsigned short id_len;
+  uint16 id_len;
 
   SILC_LOG_DEBUG(("Processing New ID List"));
 
@@ -1566,11 +1712,13 @@ void silc_server_new_channel(SilcServer server,
                             SilcSocketConnection sock,
                             SilcPacketContext *packet)
 {
-  unsigned char *id;
+  SilcChannelPayload payload;
   SilcChannelID *channel_id;
-  unsigned short channel_id_len;
   char *channel_name;
-  int ret;
+  uint32 name_len;
+  unsigned char *id;
+  uint32 id_len;
+  uint32 mode;
 
   SILC_LOG_DEBUG(("Processing New Channel"));
 
@@ -1579,23 +1727,23 @@ void silc_server_new_channel(SilcServer server,
       server->server_type == SILC_SERVER)
     return;
 
-  /* Parse payload */
-  ret = silc_buffer_unformat(packet->buffer, 
-                            SILC_STR_UI16_STRING_ALLOC(&channel_name),
-                            SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len),
-                            SILC_STR_END);
-  if (ret == -1) {
-    if (channel_name)
-      silc_free(channel_name);
-    if (id)
-      silc_free(id);
+  /* Parse the channel payload */
+  payload = silc_channel_payload_parse(packet->buffer);
+  if (!payload)
     return;
-  }
     
-  /* Decode the channel ID */
-  channel_id = silc_id_str2id(id, channel_id_len, SILC_ID_CHANNEL);
-  if (!channel_id)
+  /* Get the channel ID */
+  channel_id = silc_channel_get_id_parse(payload);
+  if (!channel_id) {
+    silc_channel_payload_free(payload);
     return;
+  }
+
+  channel_name = silc_channel_get_name(payload, &name_len);
+  if (name_len > 256)
+    channel_name[255] = '\0';
+
+  id = silc_channel_get_id(payload, &id_len);
 
   if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
     /* Add the server to global list as it is coming from router. It 
@@ -1605,8 +1753,9 @@ void silc_server_new_channel(SilcServer server,
                    silc_id_render(channel_id, SILC_ID_CHANNEL), 
                    sock->hostname));
     
-    silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id, 
-                           server->router->connection, NULL, NULL);
+    silc_idlist_add_channel(server->global_list, strdup(channel_name), 
+                           0, channel_id, server->router->connection, 
+                           NULL, NULL);
 
     server->stat.channels++;
   } else {
@@ -1633,11 +1782,14 @@ void silc_server_new_channel(SilcServer server,
       channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
                                                       channel_name,
                                                       channel_id, FALSE);
-      if (!channel)
+      if (!channel) {
+       silc_channel_payload_free(payload);
+       silc_free(channel_id);
        return;
+      }
 
       /* Send the new channel key to the server */
-      chk = silc_channel_key_payload_encode(channel_id_len, id,
+      chk = silc_channel_key_payload_encode(id_len, id,
                                            strlen(channel->channel_key->
                                                   cipher->name),
                                            channel->channel_key->cipher->name,
@@ -1651,6 +1803,10 @@ void silc_server_new_channel(SilcServer server,
       /* The channel exist by that name, check whether the ID's match.
         If they don't then we'll force the server to use the ID we have.
         We also create a new key for the channel. */
+      SilcBuffer users = NULL;
+
+      if (!channel->id)
+       channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
 
       if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
        /* They don't match, send CHANNEL_CHANGE notify to the server to
@@ -1662,33 +1818,59 @@ void silc_server_new_channel(SilcServer server,
                                               SILC_ID_CHANNEL_LEN);
       }
 
+      /* If the mode is different from what we have then enforce the
+        mode change. */
+      mode = silc_channel_get_mode(payload);
+      if (channel->mode != mode) {
+       SILC_LOG_DEBUG(("Forcing the server to change channel mode"));
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     channel->mode, server->id,
+                                     SILC_ID_SERVER, SILC_ID_SERVER_LEN,
+                                     channel->cipher, channel->hmac_name);
+      }
+
       /* Create new key for the channel and send it to the server and
         everybody else possibly on the channel. */
 
-      silc_server_create_channel_key(server, channel, 0);
-
-      /* Send to the channel */
-      silc_server_send_channel_key(server, sock, channel, FALSE);
+      if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+       silc_server_create_channel_key(server, channel, 0);
+       
+       /* Send to the channel */
+       silc_server_send_channel_key(server, sock, channel, FALSE);
+       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       id_len = SILC_ID_CHANNEL_LEN;
+       
+       /* Send to the server */
+       chk = silc_channel_key_payload_encode(id_len, id,
+                                             strlen(channel->channel_key->
+                                                    cipher->name),
+                                             channel->channel_key->
+                                             cipher->name,
+                                             channel->key_len / 8, 
+                                             channel->key);
+       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+                               chk->data, chk->len, FALSE);
+       silc_buffer_free(chk);
+       silc_free(id);
+      }
 
-      /* Send to the server */
-      chk = silc_channel_key_payload_encode(channel_id_len, id,
-                                           strlen(channel->channel_key->
-                                                  cipher->name),
-                                           channel->channel_key->cipher->name,
-                                           channel->key_len / 8, 
-                                           channel->key);
-      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
-                             chk->data, chk->len, FALSE);
-      silc_buffer_free(chk);
+      silc_free(channel_id);
 
       /* Since the channel is coming from server and we also know about it
         then send the JOIN notify to the server so that it see's our
         users on the channel "joining" the channel. */
-      /* XXX TODO **/
+      silc_server_announce_get_channel_users(server, channel, &users);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
     }
   }
 
-  silc_free(id);
+  silc_channel_payload_free(payload);
 }
 
 /* Received New Channel List packet, list of New Channel List payloads inside
@@ -1700,7 +1882,7 @@ void silc_server_new_channel_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  unsigned short len1, len2;
+  uint16 len1, len2;
 
   SILC_LOG_DEBUG(("Processing New Channel List"));
 
@@ -1748,14 +1930,14 @@ void silc_server_new_channel_list(SilcServer server,
        (len2 > buffer->truelen))
       break;
 
-    silc_buffer_pull_tail(buffer, 4 + len1 + len2);
-    silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2);
+    silc_buffer_pull_tail(buffer, 8 + len1 + len2);
+    silc_buffer_put(buffer, packet->buffer->data, 8 + len1 + len2);
 
     /* Process the New Channel */
     silc_server_new_channel(server, sock, new);
 
-    silc_buffer_push_tail(buffer, 4 + len1 + len2);
-    silc_buffer_pull(packet->buffer, 4 + len1 + len2);
+    silc_buffer_push_tail(buffer, 8 + len1 + len2);
+    silc_buffer_pull(packet->buffer, 8 + len1 + len2);
   }
 
   silc_buffer_free(buffer);
@@ -1771,10 +1953,7 @@ void silc_server_key_agreement(SilcServer server,
                               SilcSocketConnection sock,
                               SilcPacketContext *packet)
 {
-  SilcClientID *id;
-  SilcServerEntry router;
   SilcSocketConnection dst_sock;
-  SilcClientEntry client;
   SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1786,79 +1965,96 @@ void silc_server_key_agreement(SilcServer server,
   if (!packet->dst_id)
     return;
 
-  /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
-  if (!id) {
-    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
     return;
-  }
 
-  /* If the destination belongs to our server we don't have to route
-     the packet anywhere but to send it to the local destination. */
-  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
-  if (client) {
-    /* It exists, now deliver the packet to the destination */
-    dst_sock = (SilcSocketConnection)client->connection;
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac, packet, FALSE);
+}
 
-    /* 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)client->router;
-      idata = (SilcIDListData)router;
-      silc_server_send_key_agreement(server, router->connection,
-                                    idata->send_key,
-                                    idata->hmac,
-                                    packet);
-      silc_free(id);
-      return;
-    }
+/* Received connection auth request packet that is used during connection
+   phase to resolve the mandatory authentication method.  This packet can
+   actually be received at anytime but usually it is used only during
+   the connection authentication phase. Now, protocol says that this packet
+   can come from client or server, however, we support only this coming
+   from client and expect that server's always knows what authentication
+   method to use. */
+
+void silc_server_connection_auth_request(SilcServer server,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet)
+{
+  SilcServerConfigSectionClientConnection *client = NULL;
+  uint16 conn_type;
+  int ret;
+  SilcAuthMethod auth_meth;
 
-    /* Seems that client really is directly connected to us */
-    idata = (SilcIDListData)client;
-    silc_server_send_key_agreement(server, dst_sock, 
-                                  idata->send_key,
-                                  idata->hmac, packet);
-    silc_free(id);
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT)
     return;
-  }
 
-  /* Destination belongs to someone not in this server. If we are normal
-     server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
-    router = server->router;
-
-    /* Send to primary route */
-    if (router) {
-      dst_sock = (SilcSocketConnection)router->connection;
-      idata = (SilcIDListData)router;
-      silc_server_send_key_agreement(server, dst_sock, 
-                                    idata->send_key,
-                                    idata->hmac, packet);
-    }
-    silc_free(id);
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_SHORT(&conn_type),
+                            SILC_STR_UI_SHORT(NULL),
+                            SILC_STR_END);
+  if (ret == -1)
     return;
-  }
 
-  /* We are router and we will perform route lookup for the destination 
-     and send the packet to fastest route. */
-  if (server->server_type == SILC_ROUTER && !server->standalone) {
-    /* Check first that the ID is valid */
-    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
-    if (client) {
-      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
-      router = (SilcServerEntry)dst_sock->user_data;
-      idata = (SilcIDListData)router;
-
-      /* Get fastest route and send packet. */
-      if (router)
-       silc_server_send_key_agreement(server, dst_sock, 
-                                      idata->send_key,
-                                      idata->hmac, packet);
-      silc_free(id);
-      return;
-    }
-  }
+  if (conn_type != SILC_SOCKET_TYPE_CLIENT)
+    return;
+
+  /* Get the authentication method for the client */
+  auth_meth = SILC_AUTH_NONE;
+  client = silc_server_config_find_client_conn(server->config,
+                                              sock->ip,
+                                              sock->port);
+  if (!client)
+    client = silc_server_config_find_client_conn(server->config,
+                                                sock->hostname,
+                                                sock->port);
+  if (client)
+    auth_meth = client->auth_meth;
+         
+  /* Send it back to the client */
+  silc_server_send_connection_auth_request(server, sock,
+                                          conn_type,
+                                          auth_meth);
+}
+
+/* Received REKEY packet. The sender of the packet wants to regenerate
+   its session keys. This starts the REKEY protocol. */
+
+void silc_server_rekey(SilcServer server,
+                      SilcSocketConnection sock,
+                      SilcPacketContext *packet)
+{
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = TRUE;
+  proto_ctx->pfs = idata->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+                     &protocol, proto_ctx, silc_server_rekey_final);
+  sock->protocol = protocol;
+
+  if (proto_ctx->pfs == FALSE)
+    /* Run the protocol */
+    protocol->execute(server->timeout_queue, 0, protocol, sock->sock, 0, 0);
 }