Merged from silc_1_0_branch.
[silc.git] / apps / silcd / packet_send.c
index 9c721ab01e07f9bc05c20ddb18d06418323b8786..8e3ab0f248742ce3a308a3074059467eb3aec13b 100644 (file)
@@ -38,18 +38,24 @@ int silc_server_packet_send_real(SilcServer server,
   if (SILC_IS_DISCONNECTING(sock))
     return -1;
 
-  /* If rekey protocol is active we must assure that all packets are
-     sent through packet queue. */
-  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);
+  ret = silc_packet_send(sock, FALSE);
   if (ret != -2) {
+    if (ret == -1) {
+      SILC_LOG_ERROR(("Error sending packet to connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
+
+      SILC_SET_DISCONNECTING(sock);
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_server_close_connection(server, sock);
+      return ret;
+    }
+
     server->stat.packets_sent++;
     return ret;
   }
@@ -168,7 +174,8 @@ void silc_server_packet_send_dest(SilcServer server,
     return;
   }
 
-  SILC_LOG_DEBUG(("Sending %s packet", silc_get_packet_name(type)));
+  SILC_LOG_DEBUG(("Sending %s packet (forced=%s)",
+                 silc_get_packet_name(type), force_send ? "yes" : "no"));
 
   if (dst_id) {
     dst_id_data = silc_id_id2str(dst_id, dst_id_type);
@@ -179,7 +186,14 @@ void silc_server_packet_send_dest(SilcServer server,
     cipher = idata->send_key;
     hmac = idata->hmac_send;
     sequence = idata->psn_send++;
-    block_len = silc_cipher_get_block_len(cipher);
+    if (cipher)
+      block_len = silc_cipher_get_block_len(cipher);
+
+    /* Check for mandatory rekey */
+    if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+      silc_schedule_task_add(server->schedule, sock->sock,
+                            silc_server_rekey_callback, sock, 0, 1,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
   }
 
   /* Set the packet context pointers */
@@ -196,7 +210,10 @@ void silc_server_packet_send_dest(SilcServer server,
                                            packetdata.dst_id_len));
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
+  if (type == SILC_PACKET_CONNECTION_AUTH)
+    SILC_PACKET_PADLEN_MAX(packetdata.truelen, block_len, packetdata.padlen);
+  else
+    SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
   if (!silc_packet_assemble(&packetdata, NULL, cipher, hmac, sock,
@@ -215,10 +232,8 @@ void silc_server_packet_send_dest(SilcServer server,
   silc_server_packet_send_real(server, sock, force_send);
 
  out:
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
 }
 
 /* Assembles a new packet to be sent out to network. This doesn't actually
@@ -273,6 +288,12 @@ void silc_server_packet_send_srcdest(SilcServer server,
     hmac = idata->hmac_send;
     sequence = idata->psn_send++;
     block_len = silc_cipher_get_block_len(cipher);
+
+    /* Check for mandatory rekey */
+    if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+      silc_schedule_task_add(server->schedule, sock->sock,
+                            silc_server_rekey_callback, sock, 0, 1,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
   }
 
   if (dst_id) {
@@ -299,7 +320,7 @@ void silc_server_packet_send_srcdest(SilcServer server,
                                            dst_id_len));
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
+  SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
   if (!silc_packet_assemble(&packetdata, NULL, cipher, hmac, sock, data,
@@ -318,10 +339,8 @@ void silc_server_packet_send_srcdest(SilcServer server,
   silc_server_packet_send_real(server, sock, force_send);
 
  out:
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
 }
 
 /* Broadcast received packet to our primary route. This function is used
@@ -367,6 +386,12 @@ void silc_server_packet_broadcast(SilcServer server,
     /* Now actually send the packet */
     silc_server_packet_send_real(server, sock, TRUE);
     silc_free(id);
+
+    /* Check for mandatory rekey */
+    if (idata->psn_send == SILC_SERVER_REKEY_THRESHOLD)
+      silc_schedule_task_add(server->schedule, sock->sock,
+                            silc_server_rekey_callback, sock, 0, 1,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -405,6 +430,12 @@ void silc_server_packet_route(SilcServer server,
 
   /* Now actually send the packet */
   silc_server_packet_send_real(server, sock, TRUE);
+
+  /* Check for mandatory rekey */
+  if (idata->psn_send == SILC_SERVER_REKEY_THRESHOLD)
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_rekey_callback, sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* This routine can be used to send a packet to table of clients provided
@@ -516,11 +547,11 @@ silc_server_packet_send_to_channel_real(SilcServer server,
 
   block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
   if (channel_message)
-    packet->padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                        packet->src_id_len +
-                                        packet->dst_id_len), block_len);
+    SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                       packet->src_id_len +
+                       packet->dst_id_len), block_len, packet->padlen);
   else
-    packet->padlen = SILC_PACKET_PADLEN(packet->truelen, block_len);
+    SILC_PACKET_PADLEN(packet->truelen, block_len, packet->padlen);
 
   /* Put the data to buffer, assemble and encrypt the packet. The packet
      is encrypted with normal session key shared with the client, unless
@@ -716,6 +747,9 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
                                            unsigned char *data,
                                            unsigned int data_len)
 {
+  SilcUInt32 mac_len, iv_len;
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+
   /* 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
@@ -723,21 +757,27 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
      same channel key. */
   if (server->server_type == SILC_ROUTER &&
       sock->type == SILC_SOCKET_TYPE_ROUTER &&
-      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) &&
-      channel->channel_key) {
-    SilcUInt32 mac_len = silc_hmac_len(channel->hmac);
-    SilcUInt32 iv_len = silc_cipher_get_block_len(channel->channel_key);
-    unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) && channel->key) {
+
+    /* If we are backup router and remote is our primary router and
+       we are currently doing backup resuming protocol we must not
+       re-encrypt message with session key. */
+    if (server->backup_router && SILC_SERVER_IS_BACKUP(sock) &&
+       SILC_PRIMARY_ROUTE(server) == sock)
+      return TRUE;
+
+    mac_len = silc_hmac_len(channel->hmac);
+    iv_len = silc_cipher_get_block_len(channel->channel_key);
 
     if (data_len <= mac_len + iv_len) {
       SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
       return FALSE;
     }
 
-    memcpy(iv, data + (data_len - iv_len), iv_len);
-    silc_channel_message_payload_encrypt(data, data_len - iv_len - mac_len,
-                                        data_len, iv, iv_len,
-                                        channel->channel_key, channel->hmac);
+    memcpy(iv, data + (data_len - iv_len - mac_len), iv_len);
+    silc_message_payload_encrypt(data, data_len - iv_len, data_len,
+                                iv, iv_len, channel->channel_key,
+                                channel->hmac);
   }
 
   return TRUE;
@@ -886,6 +926,20 @@ void silc_server_packet_relay_to_channel(SilcServer server,
          continue;
        gone = TRUE;
 
+       /* If we are backup router and remote is our primary router and
+          we are currently doing backup resuming protocol we must not
+          re-encrypt message with session key. */
+       if (server->backup_router && SILC_SERVER_IS_BACKUP(sock) &&
+           SILC_PRIMARY_ROUTE(server) == sock) {
+         silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                                 idata->send_key,
+                                                 idata->hmac_send,
+                                                 idata->psn_send++,
+                                                 data, data_len, TRUE,
+                                                 force_send);
+         continue;
+       }
+
        SILC_LOG_DEBUG(("Remote is router, encrypt with session key"));
 
        /* If private key mode is not set then decrypt the packet
@@ -899,8 +953,9 @@ void silc_server_packet_relay_to_channel(SilcServer server,
          memcpy(tmp, data, data_len);
 
          /* Decrypt the channel message (we don't check the MAC) */
-         silc_channel_message_payload_decrypt(tmp, data_len,
-                                              channel->channel_key, NULL);
+         silc_message_payload_decrypt(tmp, data_len, FALSE, FALSE,
+                                      channel->channel_key,
+                                      channel->hmac, FALSE);
 
          /* Now re-encrypt and send it to the router */
          silc_server_packet_send_srcdest(server, sock,
@@ -984,8 +1039,8 @@ void silc_server_packet_send_local_channel(SilcServer server,
   /* Send the message to clients on the channel's client list. */
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
-    if (chl->client && !chl->client->router) {
-      sock = (SilcSocketConnection)chl->client->connection;
+    if (chl->client && SILC_IS_LOCAL(chl->client)) {
+      sock = chl->client->connection;
 
       /* Send the packet to the client */
       silc_server_packet_send_dest(server, sock, type, flags, chl->client->id,
@@ -1034,6 +1089,12 @@ void silc_server_send_private_message(SilcServer server,
 
   /* Send the packet */
   silc_server_packet_send_real(server, dst_sock, FALSE);
+
+  /* Check for mandatory rekey */
+  if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+    silc_schedule_task_add(server->schedule, dst_sock->sock,
+                          silc_server_rekey_callback, dst_sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* Sends current motd to client */
@@ -1052,6 +1113,7 @@ void silc_server_send_motd(SilcServer server,
     if (!motd)
       return;
 
+    motd[motd_len] = 0;
     silc_server_send_notify(server, sock, FALSE, SILC_NOTIFY_TYPE_MOTD, 1,
                            motd, motd_len);
     silc_free(motd);
@@ -1070,7 +1132,7 @@ void silc_server_send_error(SilcServer server,
 
   memset(buf, 0, sizeof(buf));
   va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
+  vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
   va_end(ap);
 
   silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
@@ -1225,14 +1287,13 @@ void silc_server_send_notify_cmode(SilcServer server,
                                   const char *passphrase,
                                   SilcPublicKey founder_key)
 {
-  SilcBuffer idp;
-  unsigned char mode[4], *key = NULL;
-  SilcUInt32 key_len = 0;
+  SilcBuffer idp, fkey = NULL;
+  unsigned char mode[4];
 
   idp = silc_id_payload_encode((void *)id, id_type);
   SILC_PUT32_MSB(mode_mask, mode);
   if (founder_key)
-    key = silc_pkcs_public_key_encode(founder_key, &key_len);
+    fkey = silc_pkcs_public_key_payload_encode(founder_key);
 
   silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
                               SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_CMODE_CHANGE,
@@ -1242,8 +1303,8 @@ void silc_server_send_notify_cmode(SilcServer server,
                               hmac, hmac ? strlen(hmac) : 0,
                               passphrase, passphrase ? 
                               strlen(passphrase) : 0,
-                              key, key_len);
-  silc_free(key);
+                              fkey ? fkey->data : NULL, fkey ? fkey->len : 0);
+  silc_buffer_free(fkey),
   silc_buffer_free(idp);
 }
 
@@ -1260,15 +1321,14 @@ void silc_server_send_notify_cumode(SilcServer server,
                                    SilcClientID *target,
                                    SilcPublicKey founder_key)
 {
-  SilcBuffer idp1, idp2;
-  unsigned char mode[4], *key = NULL;
-  SilcUInt32 key_len = 0;
+  SilcBuffer idp1, idp2, fkey = NULL;
+  unsigned char mode[4];
 
   idp1 = silc_id_payload_encode((void *)id, id_type);
   idp2 = silc_id_payload_encode((void *)target, SILC_ID_CLIENT);
   SILC_PUT32_MSB(mode_mask, mode);
   if (founder_key)
-    key = silc_pkcs_public_key_encode(founder_key, &key_len);
+    fkey = silc_pkcs_public_key_payload_encode(founder_key);
 
   silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
                               SILC_ID_CHANNEL, 
@@ -1276,8 +1336,8 @@ void silc_server_send_notify_cumode(SilcServer server,
                               idp1->data, idp1->len,
                               mode, 4,
                               idp2->data, idp2->len,
-                              key, key_len);
-  silc_free(key);
+                              fkey ? fkey->data : NULL, fkey ? fkey->len : 0);
+  silc_buffer_free(fkey);
   silc_buffer_free(idp1);
   silc_buffer_free(idp2);
 }
@@ -1410,7 +1470,8 @@ void silc_server_send_notify_ban(SilcServer server,
                                 SilcSocketConnection sock,
                                 bool broadcast,
                                 SilcChannelEntry channel,
-                                char *add, char *del)
+                                unsigned char *action,
+                                SilcBuffer list)
 {
   SilcBuffer idp;
 
@@ -1418,8 +1479,8 @@ void silc_server_send_notify_ban(SilcServer server,
   silc_server_send_notify(server, sock, broadcast,
                          SILC_NOTIFY_TYPE_BAN, 3,
                          idp->data, idp->len,
-                         add, add ? strlen(add) : 0,
-                         del, del ? strlen(del) : 0);
+                         action ? action : NULL, action ? 1 : 0,
+                         list ? list->data : NULL, list ? list->len : 0);
   silc_buffer_free(idp);
 }
 
@@ -1433,7 +1494,8 @@ void silc_server_send_notify_invite(SilcServer server,
                                    bool broadcast,
                                    SilcChannelEntry channel,
                                    SilcClientID *client_id,
-                                   char *add, char *del)
+                                   unsigned char *action,
+                                   SilcBuffer list)
 {
   SilcBuffer idp, idp2;
 
@@ -1444,8 +1506,8 @@ void silc_server_send_notify_invite(SilcServer server,
                          idp->data, idp->len,
                          channel->channel_name, strlen(channel->channel_name),
                          idp2->data, idp2->len,
-                         add, add ? strlen(add) : 0,
-                         del, del ? strlen(del) : 0);
+                         action ? action : NULL, action ? 1 : 0,
+                         list ? list->data : NULL, list ? list->len : 0);
   silc_buffer_free(idp);
   silc_buffer_free(idp2);
 }
@@ -1768,19 +1830,23 @@ void silc_server_send_channel_key(SilcServer server,
   SilcBuffer packet;
   unsigned char *chid;
   SilcUInt32 tmp_len;
+  const char *cipher;
  
   SILC_LOG_DEBUG(("Sending key to channel %s", channel->channel_name));
  
   chid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
   if (!chid)
     return;
+
+  if (!channel->channel_key)
+    return;
  
   /* Encode channel key packet */
-  tmp_len = strlen(channel->channel_key->cipher->name);
+  cipher = silc_cipher_get_name(channel->channel_key);
+  tmp_len = strlen(cipher);
   packet = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
                                                           SILC_ID_CHANNEL),
-                                          chid, tmp_len,
-                                           channel->channel_key->cipher->name,
+                                          chid, tmp_len, cipher,
                                            channel->key_len / 8, channel->key);
   silc_server_packet_send_to_channel(server, sender, channel, 
                                     SILC_PACKET_CHANNEL_KEY,
@@ -1806,7 +1872,7 @@ void silc_server_send_command(SilcServer server,
 
   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);
+                         packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
   va_end(ap);
 }
@@ -1857,7 +1923,7 @@ void silc_server_send_dest_command_reply(SilcServer server,
                                                 ident, argc, ap);
   silc_server_packet_send_dest(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
                               dst_id, dst_id_type, packet->data, 
-                              packet->len, TRUE);
+                              packet->len, FALSE);
   silc_buffer_free(packet);
   va_end(ap);
 }
@@ -1901,6 +1967,12 @@ void silc_server_relay_packet(SilcServer server,
 
   silc_buffer_pull(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
                   + packet->dst_id_len + packet->padlen);
+
+  /* Check for mandatory rekey */
+  if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+    silc_schedule_task_add(server->schedule, dst_sock->sock,
+                          silc_server_rekey_callback, dst_sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* Routine used to send the connection authentication packet. */
@@ -1935,7 +2007,6 @@ void silc_server_packet_queue_purge(SilcServer server,
       (SILC_IS_DISCONNECTED(sock) == FALSE)) {
     server->stat.packets_sent++;
     silc_packet_send(sock, TRUE);
-    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
     SILC_UNSET_OUTBUF_PENDING(sock);
     silc_buffer_clear(sock->outbuf);
   }