updates.
[runtime.git] / apps / silcd / packet_receive.c
index 403c5928493d82f0e81757297f43537c7ba66af1..937cc2e7eada0657c19cf2417e45b2e0437ac686 100644 (file)
@@ -42,6 +42,7 @@ void silc_server_notify(SilcServer server,
   SilcChannelEntry channel;
   SilcClientEntry client;
   SilcChannelClientEntry chl;
+  SilcIDCacheEntry cache;
   unsigned int mode;
   unsigned char *tmp;
   unsigned int tmp_len;
@@ -52,6 +53,24 @@ void silc_server_notify(SilcServer server,
       packet->src_id_type != SILC_ID_SERVER)
     return;
 
+  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
      we will broadcast it. The sending socket really cannot be router or
      the router is buggy. If this packet is coming from router then it must
@@ -90,10 +109,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);
@@ -118,10 +137,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 */
@@ -129,13 +148,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;
       }
     }
 
@@ -171,11 +192,15 @@ 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) { 
-      silc_free(channel_id);
-      goto out;
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
     }
 
     /* Get client ID */
@@ -229,10 +254,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;
@@ -246,11 +271,16 @@ void silc_server_notify(SilcServer server,
       tmp = NULL;
 
     /* Remove the client from all channels */
-    silc_server_remove_from_channels(server, NULL, client, tmp);
+    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:
@@ -266,10 +296,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);
@@ -342,7 +372,7 @@ void silc_server_notify(SilcServer server,
 
        /* Send the NICK_CHANGE notify type to local clients on the channels
           this client is joined to. */
-       silc_server_send_notify_on_channels(server, client, 
+       silc_server_send_notify_on_channels(server, NULL, client, 
                                            SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
                                            id, tmp_len, 
                                            id2, tmp_len);
@@ -367,10 +397,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);
@@ -410,10 +440,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);
@@ -468,7 +498,75 @@ 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:
@@ -492,10 +590,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);
@@ -518,10 +616,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);
@@ -534,6 +632,172 @@ void silc_server_notify(SilcServer server,
 
     break;
 
+  case SILC_NOTIFY_TYPE_KILLED:
+    {
+      /* 
+       * Distribute the notify to local clients on channels
+       */
+      unsigned char *id;
+      unsigned int 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;
+    
+    /* 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 */
   case SILC_NOTIFY_TYPE_NONE:
   case SILC_NOTIFY_TYPE_MOTD:
@@ -609,10 +873,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"));
@@ -624,78 +885,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"));
+  /* 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 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);
-      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);
-    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);
-    }
-    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);
-      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
@@ -707,10 +905,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"));
@@ -722,77 +917,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);
-      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);
+  /* 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;
-  }
 
-  /* 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);
-    }
-    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);
-      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
@@ -920,6 +1053,35 @@ void silc_server_channel_message(SilcServer server,
     }
   }
 
+  /* If we are router and the packet came from router and private key
+     has not been set for the channel then we must encrypt the packet
+     as it was decrypted with the session key shared between us and the
+     router which sent it. This is so, because cells does not share the
+     same channel key */
+  if (server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    SilcBuffer chp;
+    unsigned int iv_len, i, data_len;
+
+    iv_len = silc_cipher_get_block_len(channel->channel_key);
+    if (channel->iv[0] == '\0')
+      for (i = 0; i < iv_len; i++) channel->iv[i] = 
+                                    silc_rng_get_byte(server->rng);
+    else
+      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_message_payload_encode(data_len, 
+                                             packet->buffer->data + 2,
+                                             iv_len, channel->iv,
+                                             channel->channel_key,
+                                             channel->hmac, server->rng);
+    silc_buffer_put(packet->buffer, chp->data, chp->len);
+    silc_buffer_free(chp);
+  }
+
   /* Distribute the packet to our local clients. This will send the
      packet for further routing as well, if needed. */
   silc_server_packet_relay_to_channel(server, sock, channel, sender,
@@ -1015,6 +1177,9 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   silc_id_create_client_id(server->id, server->rng, server->md5hash,
                           username, &client_id);
 
+  if (strlen(username) > 128)
+    username[127] = '\0';
+
   /* Update client entry */
   idata->registered = TRUE;
   client->nickname = strdup(username);
@@ -1120,7 +1285,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   SilcServerID *server_id;
   SilcIDListData idata;
   unsigned char *server_name, *id_string;
-  unsigned short id_len;
+  unsigned short id_len, name_len;
   int ret;
 
   SILC_LOG_DEBUG(("Creating new server"));
@@ -1143,7 +1308,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)
@@ -1159,6 +1325,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) {
@@ -1267,13 +1436,15 @@ 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++;
@@ -1414,11 +1585,12 @@ 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;
+  unsigned int name_len;
+  unsigned char *id;
+  unsigned int id_len;
 
   SILC_LOG_DEBUG(("Processing New Channel"));
 
@@ -1427,23 +1599,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 
@@ -1453,8 +1625,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 {
@@ -1481,11 +1654,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,
@@ -1519,7 +1695,7 @@ void silc_server_new_channel(SilcServer server,
       silc_server_send_channel_key(server, sock, channel, FALSE);
 
       /* Send 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,
@@ -1528,6 +1704,7 @@ void silc_server_new_channel(SilcServer server,
       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
@@ -1535,8 +1712,6 @@ void silc_server_new_channel(SilcServer server,
       /* XXX TODO **/
     }
   }
-
-  silc_free(id);
 }
 
 /* Received New Channel List packet, list of New Channel List payloads inside
@@ -1596,14 +1771,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);
@@ -1619,10 +1794,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"));
@@ -1634,75 +1806,13 @@ 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 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_key_agreement(server, router->connection,
-                                    idata->send_key,
-                                    idata->hmac,
-                                    packet);
-      return;
-    }
-
-    /* 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);
-    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);
-    }
-    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);
-      return;
-    }
-  }
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac, packet, FALSE);
 }