updates.
[silc.git] / apps / silcd / packet_send.c
index c550fbae4e585279967c35f51a3e77be5bdc38cc..c6fcdf1ab07baf5a52f7411dbedb003265a8af0f 100644 (file)
@@ -31,7 +31,7 @@
 
 int silc_server_packet_send_real(SilcServer server,
                                 SilcSocketConnection sock,
-                                int force_send)
+                                bool force_send)
 {
   int ret;
 
@@ -44,6 +44,10 @@ int silc_server_packet_send_real(SilcServer server,
   if (SILC_SERVER_IS_REKEY(sock))
     force_send = FALSE;
 
+  /* If outbound data is already pending do not force send */
+  if (SILC_IS_OUTBUF_PENDING(sock))
+    force_send = FALSE;
+
   /* Send the packet */
   ret = silc_packet_send(sock, force_send);
   if (ret != -2)
@@ -75,10 +79,11 @@ void silc_server_packet_send(SilcServer server,
                             SilcPacketFlags flags,
                             unsigned char *data, 
                             uint32 data_len,
-                            int force_send)
+                            bool force_send)
 {
   void *dst_id = NULL;
   SilcIdType dst_id_type = SILC_ID_NONE;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
 
   if (!sock)
     return;
@@ -87,6 +92,10 @@ void silc_server_packet_send(SilcServer server,
   if (SILC_IS_DISCONNECTING(sock))
     return;
 
+  /* If entry is disabled do not sent anything. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
+
   /* Get data used in the packet sending, keys and stuff */
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
@@ -125,10 +134,10 @@ void silc_server_packet_send_dest(SilcServer server,
                                  SilcIdType dst_id_type,
                                  unsigned char *data, 
                                  uint32 data_len,
-                                 int force_send)
+                                 bool force_send)
 {
   SilcPacketContext packetdata;
-  SilcIDListData idata;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
   unsigned char *dst_id_data = NULL;
@@ -138,10 +147,11 @@ void silc_server_packet_send_dest(SilcServer server,
   if (SILC_IS_DISCONNECTING(sock))
     return;
 
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+  /* If entry is disabled do not sent anything. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
 
-  /* Get data used in the packet sending, keys and stuff */
-  idata = (SilcIDListData)sock->user_data;
+  SILC_LOG_DEBUG(("Sending packet, type %d", type));
 
   if (dst_id) {
     dst_id_data = silc_id_id2str(dst_id, dst_id_type);
@@ -218,7 +228,7 @@ void silc_server_packet_send_srcdest(SilcServer server,
                                     SilcIdType dst_id_type,
                                     unsigned char *data, 
                                     uint32 data_len,
-                                    int force_send)
+                                    bool force_send)
 {
   SilcPacketContext packetdata;
   SilcIDListData idata;
@@ -378,7 +388,7 @@ silc_server_packet_send_to_channel_real(SilcServer server,
                                        unsigned char *data,
                                        uint32 data_len,
                                        int channel_message,
-                                       int force_send)
+                                       bool force_send)
 {
   packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packet->src_id_len + packet->dst_id_len;
@@ -394,7 +404,8 @@ silc_server_packet_send_to_channel_real(SilcServer server,
   packet->buffer = sock->outbuf;
 
   /* Put the data to buffer, assemble and encrypt the packet. The packet
-     is encrypted with normal session key shared with the client. */
+     is encrypted with normal session key shared with the client, unless
+     the `channel_message' is TRUE. */
   silc_buffer_put(sock->outbuf, data, data_len);
   silc_packet_assemble(packet);
   if (channel_message)
@@ -424,10 +435,10 @@ void silc_server_packet_send_to_channel(SilcServer server,
                                        SilcSocketConnection sender,
                                        SilcChannelEntry channel,
                                        SilcPacketType type,
-                                       unsigned char route,
+                                       bool route,
                                        unsigned char *data,
                                        uint32 data_len,
-                                       int force_send)
+                                       bool force_send)
 {
   SilcSocketConnection sock = NULL;
   SilcPacketContext packetdata;
@@ -437,10 +448,10 @@ void silc_server_packet_send_to_channel(SilcServer server,
   SilcHashTableList htl;
   SilcIDListData idata;
   uint32 routed_count = 0;
+  bool gone = FALSE;
 
   /* This doesn't send channel message packets */
-  if (type == SILC_PACKET_CHANNEL_MESSAGE)
-    return;
+  assert(type != SILC_PACKET_CHANNEL_MESSAGE);
   
   SILC_LOG_DEBUG(("Sending packet to channel"));
 
@@ -459,7 +470,7 @@ void silc_server_packet_send_to_channel(SilcServer server,
 
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
-  if (route && server->server_type == SILC_SERVER && !server->standalone &&
+  if (route && server->server_type != SILC_ROUTER && !server->standalone &&
       channel->global_users) {
     SilcServerEntry router;
 
@@ -505,6 +516,13 @@ void silc_server_packet_send_to_channel(SilcServer server,
       if (sender && sock == sender)
        continue;
 
+      /* Route only once to router */
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         continue;
+       gone = TRUE;
+      }
+
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              idata->send_key, 
@@ -554,7 +572,7 @@ void silc_server_packet_send_to_channel(SilcServer server,
    then we'll need to encrypt it with the channel key. This is called
    from the silc_server_packet_relay_to_channel. */
 
-static void
+static bool
 silc_server_packet_relay_to_channel_encrypt(SilcServer server,
                                            SilcSocketConnection sock,
                                            SilcChannelEntry channel,
@@ -572,7 +590,7 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
       channel->channel_key) {
     SilcBuffer chp;
     uint32 iv_len, i;
-    uint16 data_len, flags;
+    uint16 dlen, flags;
 
     iv_len = silc_cipher_get_block_len(channel->channel_key);
     if (channel->iv[0] == '\0')
@@ -583,15 +601,22 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
     
     /* Encode new payload. This encrypts it also. */
     SILC_GET16_MSB(flags, data);
-    SILC_GET16_MSB(data_len, data + 2);
-    chp = silc_channel_message_payload_encode(flags, data_len, 
-                                             data + 4,
+    SILC_GET16_MSB(dlen, data + 2);
+
+    if (dlen > data_len) {
+      SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
+      return FALSE;
+    }
+
+    chp = silc_channel_message_payload_encode(flags, dlen, data + 4,
                                              iv_len, channel->iv,
                                              channel->channel_key,
                                              channel->hmac);
     memcpy(data, chp->data, chp->len);
     silc_buffer_free(chp);
   }
+
+  return TRUE;
 }
 
 /* This routine is explicitly used to relay messages to some channel.
@@ -611,7 +636,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
                                         void *sender_entry,
                                         unsigned char *data,
                                         uint32 data_len,
-                                        int force_send)
+                                        bool force_send)
 {
   bool found = FALSE;
   SilcSocketConnection sock = NULL;
@@ -622,9 +647,19 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   uint32 routed_count = 0;
   SilcIDListData idata;
   SilcHashTableList htl;
+  bool gone = FALSE;
 
   SILC_LOG_DEBUG(("Relaying packet to channel"));
 
+  /* This encrypts the packet, if needed. It will be encrypted if
+     it came from the router thus it needs to be encrypted with the
+     channel key. If the channel key does not exist, then we know we
+     don't have a single local user on the channel. */
+  if (!silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
+                                                  channel, data,
+                                                  data_len))
+    return;
+
   /* Set the packet context pointers. */
   packetdata.flags = 0;
   packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
@@ -638,17 +673,9 @@ void silc_server_packet_relay_to_channel(SilcServer server,
                                          packetdata.src_id_len +
                                          packetdata.dst_id_len));
 
-  /* This encrypts the packet, if needed. It will be encrypted if
-     it came from the router thus it needs to be encrypted with the
-     channel key. If the channel key does not exist, then we know we
-     don't have a single local user on the channel. */
-  silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
-                                             channel, data,
-                                             data_len);
-
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
+  if (server->server_type != SILC_ROUTER && !server->standalone &&
       channel->global_users) {
     SilcServerEntry router;
 
@@ -736,6 +763,12 @@ void silc_server_packet_relay_to_channel(SilcServer server,
           key than the remote router has. */
        if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
 
+         if (gone)
+           continue;
+
+         SILC_LOG_DEBUG(("Remote is router, encrypt with session key"));
+         gone = TRUE;
+
          /* If private key mode is not set then decrypt the packet
             and re-encrypt it */
          if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
@@ -743,9 +776,6 @@ void silc_server_packet_relay_to_channel(SilcServer server,
            memcpy(tmp, data, data_len);
 
            /* Decrypt the channel message (we don't check the MAC) */
-           /* XXX this could be optimized and removed all together by
-              taking a copy of the original data before encrypting it
-              and thus would not required decrypting. */
            if (channel->channel_key &&
                !silc_channel_message_payload_decrypt(tmp, data_len, 
                                                      channel->channel_key,
@@ -827,7 +857,7 @@ void silc_server_packet_send_local_channel(SilcServer server,
                                           SilcPacketFlags flags,
                                           unsigned char *data,
                                           uint32 data_len,
-                                          int force_send)
+                                          bool force_send)
 {
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
@@ -940,7 +970,7 @@ void silc_server_send_error(SilcServer server,
 
 void silc_server_send_notify(SilcServer server,
                             SilcSocketConnection sock,
-                            int broadcast,
+                            bool broadcast,
                             SilcNotifyType type,
                             uint32 argc, ...)
 {
@@ -953,6 +983,14 @@ void silc_server_send_notify(SilcServer server,
   silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
                          packet->data, packet->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NOTIFY, 0,
+                           packet->data, packet->len, FALSE, TRUE);
+
   silc_buffer_free(packet);
   va_end(ap);
 }
@@ -962,7 +1000,7 @@ void silc_server_send_notify(SilcServer server,
 
 void silc_server_send_notify_args(SilcServer server,
                                  SilcSocketConnection sock,
-                                 int broadcast,
+                                 bool broadcast,
                                  SilcNotifyType type,
                                  uint32 argc,
                                  SilcBuffer args)
@@ -981,7 +1019,7 @@ void silc_server_send_notify_args(SilcServer server,
 
 void silc_server_send_notify_channel_change(SilcServer server,
                                            SilcSocketConnection sock,
-                                           int broadcast,
+                                           bool broadcast,
                                            SilcChannelID *old_id,
                                            SilcChannelID *new_id)
 {
@@ -1002,7 +1040,7 @@ void silc_server_send_notify_channel_change(SilcServer server,
 
 void silc_server_send_notify_nick_change(SilcServer server,
                                         SilcSocketConnection sock,
-                                        int broadcast,
+                                        bool broadcast,
                                         SilcClientID *old_id,
                                         SilcClientID *new_id)
 {
@@ -1023,7 +1061,7 @@ void silc_server_send_notify_nick_change(SilcServer server,
 
 void silc_server_send_notify_join(SilcServer server,
                                  SilcSocketConnection sock,
-                                 int broadcast,
+                                 bool broadcast,
                                  SilcChannelEntry channel,
                                  SilcClientID *client_id)
 {
@@ -1043,7 +1081,7 @@ void silc_server_send_notify_join(SilcServer server,
 
 void silc_server_send_notify_leave(SilcServer server,
                                   SilcSocketConnection sock,
-                                  int broadcast,
+                                  bool broadcast,
                                   SilcChannelEntry channel,
                                   SilcClientID *client_id)
 {
@@ -1062,7 +1100,7 @@ void silc_server_send_notify_leave(SilcServer server,
 
 void silc_server_send_notify_cmode(SilcServer server,
                                   SilcSocketConnection sock,
-                                  int broadcast,
+                                  bool broadcast,
                                   SilcChannelEntry channel,
                                   uint32 mode_mask,
                                   void *id, SilcIdType id_type,
@@ -1089,7 +1127,7 @@ void silc_server_send_notify_cmode(SilcServer server,
 
 void silc_server_send_notify_cumode(SilcServer server,
                                    SilcSocketConnection sock,
-                                   int broadcast,
+                                   bool broadcast,
                                    SilcChannelEntry channel,
                                    uint32 mode_mask,
                                    void *id, SilcIdType id_type,
@@ -1119,7 +1157,7 @@ void silc_server_send_notify_cumode(SilcServer server,
 
 void silc_server_send_notify_signoff(SilcServer server,
                                     SilcSocketConnection sock,
-                                    int broadcast,
+                                    bool broadcast,
                                     SilcClientID *client_id,
                                     char *message)
 {
@@ -1140,7 +1178,7 @@ void silc_server_send_notify_signoff(SilcServer server,
 
 void silc_server_send_notify_topic_set(SilcServer server,
                                       SilcSocketConnection sock,
-                                      int broadcast,
+                                      bool broadcast,
                                       SilcChannelEntry channel,
                                       SilcClientID *client_id,
                                       char *topic)
@@ -1149,7 +1187,7 @@ void silc_server_send_notify_topic_set(SilcServer server,
 
   idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
   silc_server_send_notify(server, sock, broadcast,
-                         SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                         SILC_NOTIFY_TYPE_TOPIC_SET,
                          topic ? 2 : 1, 
                          idp->data, idp->len, 
                          topic, topic ? strlen(topic) : 0);
@@ -1163,7 +1201,7 @@ void silc_server_send_notify_topic_set(SilcServer server,
 
 void silc_server_send_notify_kicked(SilcServer server,
                                    SilcSocketConnection sock,
-                                   int broadcast,
+                                   bool broadcast,
                                    SilcChannelEntry channel,
                                    SilcClientID *client_id,
                                    char *comment)
@@ -1184,7 +1222,7 @@ void silc_server_send_notify_kicked(SilcServer server,
 
 void silc_server_send_notify_killed(SilcServer server,
                                    SilcSocketConnection sock,
-                                   int broadcast,
+                                   bool broadcast,
                                    SilcClientID *client_id,
                                    char *comment)
 {
@@ -1204,7 +1242,7 @@ void silc_server_send_notify_killed(SilcServer server,
 
 void silc_server_send_notify_umode(SilcServer server,
                                   SilcSocketConnection sock,
-                                  int broadcast,
+                                  bool broadcast,
                                   SilcClientID *client_id,
                                   uint32 mode_mask)
 {
@@ -1227,7 +1265,7 @@ void silc_server_send_notify_umode(SilcServer server,
 
 void silc_server_send_notify_ban(SilcServer server,
                                 SilcSocketConnection sock,
-                                int broadcast,
+                                bool broadcast,
                                 SilcChannelEntry channel,
                                 char *add, char *del)
 {
@@ -1249,7 +1287,7 @@ void silc_server_send_notify_ban(SilcServer server,
 
 void silc_server_send_notify_invite(SilcServer server,
                                    SilcSocketConnection sock,
-                                   int broadcast,
+                                   bool broadcast,
                                    SilcChannelEntry channel,
                                    SilcClientID *client_id,
                                    char *add, char *del)
@@ -1273,7 +1311,7 @@ void silc_server_send_notify_invite(SilcServer server,
 
 void silc_server_send_notify_dest(SilcServer server,
                                  SilcSocketConnection sock,
-                                 int broadcast,
+                                 bool broadcast,
                                  void *dest_id,
                                  SilcIdType dest_id_type,
                                  SilcNotifyType type,
@@ -1285,7 +1323,8 @@ void silc_server_send_notify_dest(SilcServer server,
   va_start(ap, argc);
 
   packet = silc_notify_payload_encode(type, argc, ap);
-  silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 0, 
+  silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 
+                              broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
                               dest_id, dest_id_type,
                               packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
@@ -1347,7 +1386,7 @@ void silc_server_send_notify_on_channels(SilcServer server,
   SilcBuffer packet;
   unsigned char *data;
   uint32 data_len;
-  int force_send = FALSE;
+  bool force_send = FALSE;
   va_list ap;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1478,7 +1517,7 @@ void silc_server_send_notify_on_channels(SilcServer server,
 
 void silc_server_send_new_id(SilcServer server,
                             SilcSocketConnection sock,
-                            int broadcast,
+                            bool broadcast,
                             void *id, SilcIdType id_type, 
                             uint32 id_len)
 {
@@ -1490,6 +1529,14 @@ void silc_server_send_new_id(SilcServer server,
   silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          idp->data, idp->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_ID, 0,
+                           idp->data, idp->len, FALSE, TRUE);
+
   silc_buffer_free(idp);
 }
 
@@ -1500,7 +1547,7 @@ void silc_server_send_new_id(SilcServer server,
 
 void silc_server_send_new_channel(SilcServer server,
                                  SilcSocketConnection sock,
-                                 int broadcast,
+                                 bool broadcast,
                                  char *channel_name,
                                  void *channel_id, 
                                  uint32 channel_id_len,
@@ -1524,6 +1571,13 @@ void silc_server_send_new_channel(SilcServer server,
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          packet->data, packet->len, FALSE);
 
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+                           packet->data, packet->len, FALSE, TRUE);
+
   silc_free(cid);
   silc_buffer_free(packet);
 }
@@ -1559,7 +1613,6 @@ void silc_server_send_channel_key(SilcServer server,
                                           chid, tmp_len,
                                            channel->channel_key->cipher->name,
                                            channel->key_len / 8, channel->key);
   silc_server_packet_send_to_channel(server, sender, channel, 
                                     SILC_PACKET_CHANNEL_KEY,
                                      route, packet->data, packet->len, FALSE);
@@ -1573,6 +1626,7 @@ void silc_server_send_channel_key(SilcServer server,
 void silc_server_send_command(SilcServer server, 
                              SilcSocketConnection sock,
                              SilcCommand command, 
+                             uint16 ident,
                              uint32 argc, ...)
 {
   SilcBuffer packet;
@@ -1580,7 +1634,7 @@ void silc_server_send_command(SilcServer server,
 
   va_start(ap, argc);
 
-  packet = silc_command_payload_encode_vap(command, 0, argc, ap);
+  packet = silc_command_payload_encode_vap(command, ident, argc, ap);
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND, 0,
                          packet->data, packet->len, TRUE);
   silc_buffer_free(packet);
@@ -1604,7 +1658,7 @@ void silc_server_relay_packet(SilcServer server,
                              SilcCipher cipher,
                              SilcHmac hmac,
                              SilcPacketContext *packet,
-                             int force_send)
+                             bool force_send)
 {
   silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
                   + packet->dst_id_len + packet->padlen);
@@ -1642,3 +1696,25 @@ void silc_server_send_connection_auth_request(SilcServer server,
                          0, packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
 }
+
+/* Purge the outgoing packet queue to the network if there is data. This
+   function can be used to empty the packet queue. It is guaranteed that
+   after this function returns the outgoing data queue is empty. */
+
+void silc_server_packet_queue_purge(SilcServer server,
+                                   SilcSocketConnection sock)
+{
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    server->stat.packets_sent++;
+
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
+}