updates
[silc.git] / apps / silcd / packet_receive.c
index 3d4abebcf1c36ab3657b110a5bc78ce1b976ca3e..5cac18a4bc1695a4bd851544e07a3b245e662a79 100644 (file)
@@ -45,10 +45,8 @@ void silc_server_private_message(SilcServer server,
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!packet->dst_id) {
-    SILC_LOG_ERROR(("Bad Client ID in private message packet, dropped"));
+  if (!packet->dst_id)
     goto err;
-  }
 
   /* Decode destination Client ID */
   id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
@@ -59,7 +57,7 @@ void silc_server_private_message(SilcServer server,
 
   /* 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);
+  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;
@@ -72,7 +70,7 @@ void silc_server_private_message(SilcServer server,
         we will send the packet to that server. */
       router = (SilcServerEntry)dst_sock->user_data;
       idata = (SilcIDListData)router;
-      //      assert(client->router == server->id_entry);
+      //assert(client->router == server->id_entry);
 
       silc_server_send_private_message(server, dst_sock,
                                       idata->send_key,
@@ -108,16 +106,20 @@ void silc_server_private_message(SilcServer server,
   /* We are router and we will perform route lookup for the destination 
      and send the message to fastest route. */
   if (server->server_type == SILC_ROUTER && !server->standalone) {
-    dst_sock = silc_server_get_route(server, id, SILC_ID_CLIENT);
-    router = (SilcServerEntry)dst_sock->user_data;
-    idata = (SilcIDListData)router;
+    /* 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;
+      /* Get fastest route and send packet. */
+      if (router)
+       silc_server_send_private_message(server, dst_sock, 
+                                        idata->send_key,
+                                        idata->hmac, packet);
+      return;
+    }
   }
 
  err:
@@ -125,21 +127,19 @@ void silc_server_private_message(SilcServer server,
                         "No such nickname: Private message not sent");
 }
 
-/* Relays received command reply packet to the correct destination. The
-   destination must be one of our locally connected client or the packet
-   will be ignored. This is called when server has forwarded one of
-   client's command request to router and router has now replied to the 
-   command. */
+/* Processes incoming command reply packet. The command reply packet may
+   be destined to one of our clients or it may directly for us. We will 
+   call the command reply routine after processing the packet. */
 
-void silc_server_packet_relay_command_reply(SilcServer server,
-                                           SilcSocketConnection sock,
-                                           SilcPacketContext *packet)
+void silc_server_command_reply(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcClientEntry client;
-  SilcClientID *id;
+  SilcClientEntry client = NULL;
   SilcSocketConnection dst_sock;
   SilcIDListData idata;
+  SilcClientID *id = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -148,42 +148,52 @@ void silc_server_packet_relay_command_reply(SilcServer server,
       sock->type != SILC_SOCKET_TYPE_ROUTER)
     return;
 
-  /* Destination must be client */
-  if (packet->dst_id_type != SILC_ID_CLIENT)
+  if (packet->dst_id_type == SILC_ID_CHANNEL)
     return;
 
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
-
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
-
-  /* Destination must be one of ours */
-  client = silc_idlist_find_client_by_id(server->local_list, id);
-  if (!client) {
-    SILC_LOG_ERROR(("Cannot relay command reply to unknown client"));
-    silc_free(id);
-    return;
+  if (packet->dst_id_type == SILC_ID_CLIENT) {
+    /* Destination must be one of ours */
+    id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+    client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+    if (!client) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
+      silc_free(id);
+      return;
+    }
   }
 
-  /* Relay the packet to the client */
-
-  dst_sock = (SilcSocketConnection)client->connection;
-  silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                  + packet->dst_id_len + packet->padlen);
-
-  silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-  silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+  if (packet->dst_id_type == SILC_ID_SERVER) {
+    /* For now this must be for us */
+    if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
+      return;
+    }
+  }
 
-  idata = (SilcIDListData)client;
+  /* Execute command reply locally for the command */
+  silc_server_command_reply_process(server, sock, buffer);
 
-  /* Encrypt packet */
-  silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
-                     buffer->len);
+  if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
+    /* Relay the packet to the client */
+    
+    dst_sock = (SilcSocketConnection)client->connection;
+    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                    + packet->dst_id_len + packet->padlen);
+    
+    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+    
+    idata = (SilcIDListData)client;
     
-  /* Send the packet */
-  silc_server_packet_send_real(server, dst_sock, TRUE);
+    /* Encrypt packet */
+    silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf, 
+                       buffer->len);
+    
+    /* Send the packet */
+    silc_server_packet_send_real(server, dst_sock, TRUE);
 
-  silc_free(id);
+    silc_free(id);
+  }
 }
 
 /* Process received channel message. The message can be originated from
@@ -209,7 +219,7 @@ void silc_server_channel_message(SilcServer server,
 
   /* Find channel entry */
   id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
+  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
     SILC_LOG_DEBUG(("Could not find channel"));
     goto out;
@@ -279,7 +289,7 @@ void silc_server_channel_key(SilcServer server,
     goto out;
 
   /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
+  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
     SILC_LOG_ERROR(("Received key for non-existent channel"));
     goto out;
@@ -295,7 +305,7 @@ void silc_server_channel_key(SilcServer server,
 
   /* Remove old key if exists */
   if (channel->key) {
-    memset(channel->key, 0, channel->key_len);
+    memset(channel->key, 0, channel->key_len / 8);
     silc_free(channel->key);
     silc_cipher_free(channel->channel_key);
     exist = TRUE;
@@ -598,6 +608,7 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
   SilcSocketConnection router_sock;
   SilcIDPayload idp;
   SilcIdType id_type;
+  unsigned char *hash = NULL;
   void *id, *tmpid;
 
   SILC_LOG_DEBUG(("Processing new ID"));
@@ -664,20 +675,41 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
 
   switch(id_type) {
   case SILC_ID_CLIENT:
-    SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
-                   silc_id_render(id, SILC_ID_CLIENT),
-                   sock->type == SILC_SOCKET_TYPE_SERVER ?
-                   "Server" : "Router", sock->hostname));
+    {
+      SilcClientEntry entry;
+
+      SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_CLIENT),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
     
-    /* Add the client to our local list. We are router and we keep
-       cell specific local database of all clients in the cell. */
-    silc_idlist_add_client(id_list, NULL, NULL, NULL, id, router, router_sock);
+      /* As a router we keep information of all global information in our
+        global list. Cell wide information however is kept in the local
+        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), 
+                        sizeof(unsigned char));
+      memcpy(hash, ((SilcClientID *)id)->hash, 
+            sizeof(((SilcClientID *)id)->hash));
+      entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id, 
+                                    router, router_sock);
+      entry->nickname = NULL;
 
-    /* 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);
+#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;
 
   case SILC_ID_SERVER:
@@ -686,16 +718,18 @@ void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
                    sock->type == SILC_SOCKET_TYPE_SERVER ?
                    "Server" : "Router", sock->hostname));
     
-    /* Add the server to our local list. We are router and we keep
-       cell specific local database of all servers in the cell. */
+    /* As a router we keep information of all global information in our global
+       list. Cell wide information however is kept in the local list. */
     silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
 
+#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:
@@ -760,13 +794,14 @@ void silc_server_remove_channel_user(SilcServer server,
 
   /* XXX routers should check server->global_list as well */
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
   if (!channel)
     goto out;
   
   /* XXX routers should check server->global_list as well */
   /* Get client entry */
-  client = silc_idlist_find_client_by_id(server->local_list, client_id);
+  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
   if (!client)
     goto out;
 
@@ -877,7 +912,8 @@ void silc_server_notify(SilcServer server,
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
     if (!channel) {
       silc_free(channel_id);
       goto out;
@@ -981,7 +1017,7 @@ void silc_server_new_channel_user(SilcServer server,
   silc_free(tmpid);
 
   /* Find the channel */
-  channel = silc_idlist_find_channel_by_id(id_list, channel_id);
+  channel = silc_idlist_find_channel_by_id(id_list, channel_id, NULL);
   if (!channel) {
     SILC_LOG_ERROR(("Received channel user for non-existent channel"));
     goto out;
@@ -998,7 +1034,7 @@ void silc_server_new_channel_user(SilcServer server,
   }
 
   /* Get client entry */
-  client = silc_idlist_find_client_by_id(id_list, client_id);
+  client = silc_idlist_find_client_by_id(id_list, client_id, NULL);
   if (!client) {
     /* This is new client to us, add entry to ID list */
     client = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
@@ -1035,3 +1071,96 @@ void silc_server_new_channel_user(SilcServer server,
   silc_free(tmpid1);
   silc_free(tmpid2);
 }
+
+/* Processes incoming REMOVE_ID packet. The packet is used to notify routers
+   that certain ID should be removed. After that the ID will become invalid. */
+
+void silc_server_remove_id(SilcServer server,
+                          SilcSocketConnection sock,
+                          SilcPacketContext *packet)
+{
+  SilcIDList id_list;
+  SilcIDPayload idp;
+  SilcIdType id_type;
+  void *id, *id_entry;
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      server->server_type == SILC_SERVER ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  idp = silc_id_payload_parse(packet->buffer);
+  if (!idp)
+    return;
+
+  id_type = silc_id_payload_get_type(idp);
+
+  id = silc_id_payload_get_id(idp);
+  if (!id)
+    goto out;
+
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received Remove ID packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data, packet->buffer->len, FALSE);
+  }
+
+  if (sock->type == SILC_SOCKET_TYPE_SERVER)
+    id_list = server->local_list;
+  else
+    id_list = server->global_list;
+
+  /* Remove the ID */
+  switch(id_type) {
+  case SILC_ID_CLIENT:
+    id_entry = silc_idlist_find_client_by_id(id_list, (SilcClientID *)id, 
+                                            NULL);
+    if (id_entry) {
+      silc_idlist_del_client(id_list, (SilcClientEntry)id_entry);
+
+      SILC_LOG_DEBUG(("Removed client id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_CLIENT),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    }
+    break;
+
+  case SILC_ID_SERVER:
+    id_entry = silc_idlist_find_server_by_id(id_list, (SilcServerID *)id,
+                                            NULL);
+    if (id_entry) {
+      silc_idlist_del_server(id_list, (SilcServerEntry)id_entry);
+
+      SILC_LOG_DEBUG(("Removed server id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_SERVER),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    }
+    break;
+
+  case SILC_ID_CHANNEL:
+    id_entry = silc_idlist_find_channel_by_id(id_list, (SilcChannelID *)id,
+                                             NULL);
+    if (id_entry) {
+      silc_idlist_del_channel(id_list, (SilcChannelEntry)id_entry);
+
+      SILC_LOG_DEBUG(("Removed channel id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_CHANNEL),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    }
+    break;
+
+  default:
+    break;
+  }
+
+ out:
+  silc_id_payload_free(idp);
+}