updates.
[silc.git] / apps / silcd / packet_receive.c
index 47d7cf0a135fba1b4bdc0c013bc1a71fe9290946..926163899005ea30c40485d2b411a3e4c16db780 100644 (file)
@@ -71,11 +71,13 @@ void silc_server_notify(SilcServer server,
     if (dst_sock)
       /* Relay the packet */
       silc_server_relay_packet(server, dst_sock, idata->send_key,
-                              idata->hmac_receive, packet, TRUE);
+                              idata->hmac_receive, idata->psn_send++,
+                              packet, TRUE);
   }
 
   /* Parse the Notify Payload */
-  payload = silc_notify_payload_parse(packet->buffer);
+  payload = silc_notify_payload_parse(packet->buffer->data,
+                                     packet->buffer->len);
   if (!payload)
     return;
 
@@ -178,7 +180,7 @@ void silc_server_notify(SilcServer server,
        client = 
          silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
                                 silc_id_dup(client_id, SILC_ID_CLIENT), 
-                                sock->user_data, NULL);
+                                sock->user_data, NULL, 0);
        if (!client) {
          SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
          silc_free(client_id);
@@ -365,10 +367,8 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
 
-    if (channel->topic)
-      silc_free(channel->topic);
-    channel->topic = silc_calloc(tmp_len + 1, sizeof(*channel->topic));
-    memcpy(channel->topic, tmp, tmp_len);
+    silc_free(channel->topic);
+    channel->topic = strdup(tmp);
 
     /* Send the same notify to the channel */
     silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
@@ -510,13 +510,21 @@ void silc_server_notify(SilcServer server,
 
       /* 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, 
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
+                    channel->key_len / 8, 
                     hash);
       silc_hmac_set_key(channel->hmac, hash, 
-                       silc_hash_len(channel->hmac->hash));
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
     }
 
+    /* Get the passphrase */
+    tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+    if (tmp) {
+      silc_free(channel->passphrase);
+      channel->passphrase = strdup(tmp);
+    }
+
     break;
 
   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
@@ -797,6 +805,15 @@ void silc_server_notify(SilcServer server,
                                     users_modes->len, FALSE);
        silc_buffer_free(users_modes);
       }
+
+      /* Re-announce channel's topic */
+      if (channel->topic) {
+       silc_server_send_notify_topic_set(server, sock,
+                                         server->server_type == SILC_ROUTER ?
+                                         TRUE : FALSE, channel, 
+                                         channel->id, SILC_ID_CHANNEL,
+                                         channel->topic);
+      }
     }
 
     silc_free(channel_id);
@@ -825,6 +842,48 @@ void silc_server_notify(SilcServer server,
       server_entry = silc_idlist_find_server_by_id(server->local_list, 
                                                   server_id, TRUE, NULL);
       if (!server_entry) {
+       /* If we are normal server then we might not have the server. Check
+          whether router was kind enough to send the list of all clients
+          that actually was to be removed. Remove them if the list is
+          available. */
+       if (server->server_type != SILC_ROUTER &&
+           silc_argument_get_arg_num(args) > 1) {
+         int i;
+
+         for (i = 1; i < silc_argument_get_arg_num(args); i++) {
+           /* Get Client ID */
+           tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
+           if (!tmp)
+             continue;
+           client_id = silc_id_payload_parse_id(tmp, tmp_len);
+           if (!client_id)
+             continue;
+
+           /* Get client entry */
+           client = silc_idlist_find_client_by_id(server->global_list, 
+                                                  client_id, TRUE, &cache);
+           if (!client) {
+             client = silc_idlist_find_client_by_id(server->local_list, 
+                                                    client_id, TRUE, &cache);
+             if (!client) {
+               silc_free(client_id);
+               continue;
+             }
+           }
+           silc_free(client_id);
+
+           /* Remove the client from all channels. */
+           silc_server_remove_from_channels(server, NULL, client, 
+                                            TRUE, NULL, FALSE);
+           
+           client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+           cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+           server->stat.clients--;
+           if (server->server_type == SILC_ROUTER)
+             server->stat.cell_clients--;
+         }
+       }
+
        silc_free(server_id);
        goto out;
       }
@@ -1146,21 +1205,45 @@ void silc_server_private_message(SilcServer server,
   SILC_LOG_DEBUG(("Start"));
 
   if (packet->src_id_type != SILC_ID_CLIENT ||
-      packet->dst_id_type != SILC_ID_CLIENT)
-    return;
-
-  if (!packet->dst_id)
+      packet->dst_id_type != SILC_ID_CLIENT || !packet->dst_id)
     return;
 
   /* 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)
+  if (!dst_sock) {
+    /* Send IDENTIFY command reply with error status to indicate that
+       such destination ID does not exist or is invalid */
+    SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id,
+                                                packet->dst_id_len,
+                                                packet->dst_id_type);
+    if (!idp)
+      return;
+
+    if (packet->src_id_type == SILC_ID_CLIENT) {
+      SilcClientID *client_id = silc_id_str2id(packet->src_id,
+                                              packet->src_id_len,
+                                              packet->src_id_type);
+      silc_server_send_dest_command_reply(server, sock, 
+                                         client_id, SILC_ID_CLIENT,
+                                         SILC_COMMAND_IDENTIFY,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
+                                         0, 1, 2, idp->data, idp->len);
+      silc_free(client_id);
+    } else {
+      silc_server_send_command_reply(server, sock, SILC_COMMAND_IDENTIFY,
+                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
+                                    0, 1, 2, idp->data, idp->len);
+    }
+
+    silc_buffer_free(idp);
     return;
+  }
 
   /* Send the private message */
   silc_server_send_private_message(server, dst_sock, idata->send_key,
-                                  idata->hmac_send, packet);
+                                  idata->hmac_send, idata->psn_send++,
+                                  packet);
 }
 
 /* Received private message key packet.. This packet is never for us. It is to
@@ -1192,7 +1275,7 @@ void silc_server_private_message_key(SilcServer server,
 
   /* Relay the packet */
   silc_server_relay_packet(server, dst_sock, idata->send_key,
-                          idata->hmac_send, packet, FALSE);
+                          idata->hmac_send, idata->psn_send++, packet, FALSE);
 }
 
 /* Processes incoming command reply packet. The command reply packet may
@@ -1256,8 +1339,8 @@ void silc_server_command_reply(SilcServer server,
     idata = (SilcIDListData)client;
     
     /* Encrypt packet */
-    silc_packet_encrypt(idata->send_key, idata->hmac_send, dst_sock->outbuf, 
-                       buffer->len);
+    silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
+                       dst_sock->outbuf, buffer->len);
     
     /* Send the packet */
     silc_server_packet_send_real(server, dst_sock, TRUE);
@@ -1277,6 +1360,7 @@ void silc_server_channel_message(SilcServer server,
   SilcChannelID *id = NULL;
   void *sender = NULL;
   void *sender_entry = NULL;
+  bool local = TRUE;
 
   SILC_LOG_DEBUG(("Processing channel message"));
 
@@ -1308,14 +1392,25 @@ void silc_server_channel_message(SilcServer server,
   if (packet->src_id_type == SILC_ID_CLIENT) {
     sender_entry = silc_idlist_find_client_by_id(server->local_list, 
                                                 sender, TRUE, NULL);
-    if (!sender_entry)
+    if (!sender_entry) {
+      local = FALSE;
       sender_entry = silc_idlist_find_client_by_id(server->global_list, 
                                                   sender, TRUE, NULL);
+    }
     if (!sender_entry || !silc_server_client_on_channel(sender_entry, 
                                                        channel)) {
       SILC_LOG_DEBUG(("Client not on channel"));
       goto out;
     }
+
+    /* If the packet is coming from router, but the client entry is
+       local entry to us then some router is rerouting this to us and it is
+       not allowed. */
+    if (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER && local) {
+      SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it"));
+      goto out;
+    }
   }
 
   /* Distribute the packet to our local clients. This will send the
@@ -1391,7 +1486,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   client = (SilcClientEntry)sock->user_data;
   idata = (SilcIDListData)client;
 
-  /* Remove the old cache entry */
+  /* Remove the old cache entry. */
   if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
     SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
@@ -1529,7 +1624,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
 
   /* Add the client again to the ID cache */
   silc_idcache_add(server->local_list->clients, client->nickname,
-                  client_id, client, FALSE);
+                  client_id, client, 0, NULL);
 
   /* Notify our router about new client on the SILC network */
   if (!server->standalone)
@@ -1624,6 +1719,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   unsigned char *server_name, *id_string;
   uint16 id_len, name_len;
   int ret;
+  bool local = TRUE;
 
   SILC_LOG_DEBUG(("Creating new server"));
 
@@ -1636,7 +1732,10 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   idata = (SilcIDListData)new_server;
 
   /* Remove the old cache entry */
-  silc_idcache_del_by_context(server->local_list->servers, new_server);
+  if (!silc_idcache_del_by_context(server->local_list->servers, new_server)) {
+    silc_idcache_del_by_context(server->global_list->servers, new_server);
+    local = FALSE;
+  }
 
   /* Parse the incoming packet */
   ret = silc_buffer_unformat(buffer,
@@ -1691,8 +1790,9 @@ SilcServerEntry silc_server_new_server(SilcServer server,
                  silc_id_render(server_id, SILC_ID_SERVER)));
 
   /* Add again the entry to the ID cache. */
-  silc_idcache_add(server->local_list->servers, server_name, server_id, 
-                  new_server, FALSE);
+  silc_idcache_add(local ? server->local_list->servers : 
+                  server->global_list->servers, server_name, server_id, 
+                  new_server, 0, NULL);
 
   /* Distribute the information about new server in the SILC network
      to our router. If we are normal server we won't send anything
@@ -1725,9 +1825,17 @@ SilcServerEntry silc_server_new_server(SilcServer server,
                            packet->data, packet->len, TRUE);
     silc_buffer_free(packet);
 
-    /* Mark the server disabled. The data sent earlier will go but nothing
+    /* Mark the router disabled. The data sent earlier will go but nothing
        after this does not go to this connection. */
     idata->status |= SILC_IDLIST_STATUS_DISABLED;
+  } else {
+    /* If it is router announce our stuff to it. */
+    if (sock->type == SILC_SOCKET_TYPE_ROUTER && 
+       server->server_type == SILC_ROUTER) {
+      silc_server_announce_servers(server, FALSE, 0, sock);
+      silc_server_announce_clients(server, 0, sock);
+      silc_server_announce_channels(server, 0, sock);
+    }
   }
 
   return new_server;
@@ -1743,7 +1851,7 @@ static void silc_server_new_id_real(SilcServer server,
 {
   SilcBuffer buffer = packet->buffer;
   SilcIDList id_list;
-  SilcServerEntry router;
+  SilcServerEntry router, server_entry;
   SilcSocketConnection router_sock;
   SilcIDPayload idp;
   SilcIdType id_type;
@@ -1756,31 +1864,38 @@ static void silc_server_new_id_real(SilcServer server,
       packet->src_id_type != SILC_ID_SERVER)
     return;
 
-  idp = silc_id_payload_parse(buffer);
+  idp = silc_id_payload_parse(buffer->data, buffer->len);
   if (!idp)
     return;
 
   id_type = silc_id_payload_get_type(idp);
 
   /* Normal server cannot have other normal server connections */
-  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
+  server_entry = (SilcServerEntry)sock->user_data;
+  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER &&
+      server_entry->server_type == SILC_SERVER)
     goto out;
 
   id = silc_id_payload_get_id(idp);
   if (!id)
     goto out;
 
-  if (sock->type == SILC_SOCKET_TYPE_SERVER)
-    id_list = server->local_list;
-  else
-    id_list = server->global_list;
-
   /* If the packet is coming from server then use the sender as the
      origin of the the packet. If it came from router then check the real
      sender of the packet and use that as the origin. */
   if (sock->type == SILC_SOCKET_TYPE_SERVER) {
+    id_list = server->local_list;
     router_sock = sock;
     router = sock->user_data;
+
+    /* If the sender is backup router and ID is server (and we are not
+       backup router) then switch the entry to global list. */
+    if (server_entry->server_type == SILC_BACKUP_ROUTER && 
+       id_type == SILC_ID_SERVER && 
+       server->id_entry->server_type != SILC_BACKUP_ROUTER) {
+      id_list = server->global_list;
+      router_sock = server->router ? server->router->connection : sock;
+    }
   } else {
     void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
                                     packet->src_id_type);
@@ -1793,6 +1908,7 @@ static void silc_server_new_id_real(SilcServer server,
     if (!router)
       goto out;
     router_sock = sock;
+    id_list = server->global_list;
   }
 
   switch(id_type) {
@@ -1822,7 +1938,7 @@ static void silc_server_new_id_real(SilcServer server,
         global list. Cell wide information however is kept in the local
         list. */
       entry = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
-                                    id, router, NULL);
+                                    id, router, NULL, 0);
       if (!entry) {
        SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
 
@@ -2025,7 +2141,8 @@ void silc_server_new_channel(SilcServer server,
     return;
 
   /* Parse the channel payload */
-  payload = silc_channel_payload_parse(packet->buffer);
+  payload = silc_channel_payload_parse(packet->buffer->data,
+                                      packet->buffer->len);
   if (!payload)
     return;
     
@@ -2060,7 +2177,7 @@ void silc_server_new_channel(SilcServer server,
                      sock->hostname));
     
       silc_idlist_add_channel(server->global_list, strdup(channel_name), 
-                             0, channel_id, sock->user_data, NULL, NULL);
+                             0, channel_id, sock->user_data, NULL, NULL, 0);
       server->stat.channels++;
     }
   } else {
@@ -2129,9 +2246,6 @@ void silc_server_new_channel(SilcServer server,
         We also create a new key for the channel. */
       SilcBuffer users = NULL, users_modes = 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
           force the ID change. */
@@ -2148,7 +2262,8 @@ void silc_server_new_channel(SilcServer server,
        silc_server_send_notify_cmode(server, sock, FALSE, channel,
                                      channel->mode, server->id,
                                      SILC_ID_SERVER,
-                                     channel->cipher, channel->hmac_name);
+                                     channel->cipher, channel->hmac_name,
+                                     channel->passphrase);
       }
 
       /* Create new key for the channel and send it to the server and
@@ -2310,7 +2425,8 @@ void silc_server_key_agreement(SilcServer server,
 
   /* Relay the packet */
   silc_server_relay_packet(server, dst_sock, idata->send_key,
-                          idata->hmac_send, packet, FALSE);
+                          idata->hmac_send, idata->psn_send++,
+                          packet, FALSE);
 }
 
 /* Received connection auth request packet that is used during connection
@@ -2396,3 +2512,36 @@ void silc_server_rekey(SilcServer server,
     /* Run the protocol */
     silc_protocol_execute(protocol, server->schedule, 0, 0);
 }
+
+/* Received file transger packet. This packet is never for us. It is to
+   the client in the packet's destination ID. Sending of this sort of packet
+   equals sending private message, ie. it is sent point to point from
+   one client to another. */
+
+void silc_server_ftp(SilcServer server,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet)
+{
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
+    return;
+
+  if (!packet->dst_id)
+    return;
+
+  /* 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;
+
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac_send, idata->psn_send++,
+                          packet, FALSE);
+}