Channel/Private Message unify updates.
[silc.git] / apps / silcd / packet_send.c
index 65637c159c79e73438fa85cf8e658e8f0a7173df..cc0bf7589d71afa240d7581fc325377deff60414 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  packet_send.c
+  packet_send.c 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
@@ -8,9 +8,8 @@
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -96,8 +95,11 @@ 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) ||
+  /* If entry is disabled do not sent anything.  Allow hearbeat and
+     rekeys, though */
+  if ((idata && idata->status & SILC_IDLIST_STATUS_DISABLED &&
+       type != SILC_PACKET_HEARTBEAT && type != SILC_PACKET_REKEY && 
+       type != SILC_PACKET_REKEY_DONE) ||
       sock->user_data == server->id_entry) {
     SILC_LOG_DEBUG(("Connection is disabled"));
     return;
@@ -166,7 +168,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);
@@ -177,7 +180,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 */
@@ -194,7 +204,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,
@@ -213,10 +226,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
@@ -271,6 +282,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) {
@@ -297,7 +314,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,
@@ -316,10 +333,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
@@ -365,6 +380,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;
   }
 
@@ -403,6 +424,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
@@ -514,11 +541,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
@@ -732,10 +759,9 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
       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, iv, iv_len,
+                                channel->channel_key, channel->hmac);
   }
 
   return TRUE;
@@ -884,6 +910,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
@@ -897,8 +937,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,
@@ -982,8 +1023,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,
@@ -1032,6 +1073,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 */
@@ -1050,6 +1097,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);
@@ -1068,7 +1116,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, 
@@ -1223,14 +1271,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,
@@ -1240,8 +1287,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);
 }
 
@@ -1258,15 +1305,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, 
@@ -1274,8 +1320,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);
 }
@@ -1766,19 +1812,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,
@@ -1804,7 +1854,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);
 }
@@ -1855,7 +1905,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);
 }
@@ -1899,6 +1949,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. */