Added silc_packet_error_string.
[silc.git] / lib / silccore / silcpacket.c
index 661fbbe47e53eff3bee4c8c0c19035ae1ef43ea2..ccd821198d9f757f075c265f144911fa3e206973 100644 (file)
@@ -623,6 +623,45 @@ void silc_packet_engine_stop(SilcPacketEngine engine)
   silc_free(engine);
 }
 
+static const char *packet_error[] = {
+  "Cannot read from stream",
+  "Cannot write to stream",
+  "Packet MAC failed",
+  "Packet decryption failed",
+  "Unknown SID",
+  "Packet is malformed",
+  "System out of memory",
+};
+
+/* Return packet error string */
+
+const char *silc_packet_error_string(SilcPacketError error)
+{
+  if (error < SILC_PACKET_ERR_READ || error > SILC_PACKET_ERR_NO_MEMORY)
+    return "";
+  return packet_error[error];
+}
+
+/* Return list of packet streams in the engine */
+
+SilcDList silc_packet_engine_get_streams(SilcPacketEngine engine)
+{
+  SilcDList list;
+  SilcPacketStream ps;
+
+  list = silc_dlist_init();
+  if (!list)
+    return NULL;
+
+  silc_mutex_lock(engine->lock);
+  silc_list_start(engine->streams);
+  while ((ps = silc_list_get(engine->streams)))
+    silc_dlist_add(list, ps);
+  silc_mutex_unlock(engine->lock);
+
+  return list;
+}
+
 /* Create new packet stream */
 
 SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine,
@@ -825,6 +864,8 @@ SilcPacketStream silc_packet_stream_add_remote(SilcPacketStream stream,
 
 void silc_packet_stream_destroy(SilcPacketStream stream)
 {
+  SilcPacketEngine engine;
+
   if (!stream)
     return;
 
@@ -841,17 +882,17 @@ void silc_packet_stream_destroy(SilcPacketStream stream)
 
   if (!stream->udp) {
     /* Delete from engine */
-    silc_mutex_lock(stream->sc->engine->lock);
-    silc_list_del(stream->sc->engine->streams, stream);
+    engine = stream->sc->engine;
+    silc_mutex_lock(engine->lock);
+    silc_list_del(engine->streams, stream);
 
     /* Remove per scheduler context, if it is not used anymore */
     if (stream->sc) {
       stream->sc->stream_count--;
       if (!stream->sc->stream_count)
-       silc_hash_table_del(stream->sc->engine->contexts,
-                           stream->sc->schedule);
+       silc_hash_table_del(engine->contexts, stream->sc->schedule);
     }
-    silc_mutex_unlock(stream->sc->engine->lock);
+    silc_mutex_unlock(engine->lock);
 
     /* Destroy the underlaying stream */
     if (stream->stream)
@@ -859,11 +900,13 @@ void silc_packet_stream_destroy(SilcPacketStream stream)
   } else {
     /* Delete from UDP remote hash table */
     char tuple[64];
-    silc_snprintf(tuple, sizeof(tuple), "%d%s", stream->remote_udp->remote_port,
-            stream->remote_udp->remote_ip);
-    silc_mutex_lock(stream->sc->engine->lock);
-    silc_hash_table_del(stream->sc->engine->udp_remote, tuple);
-    silc_mutex_unlock(stream->sc->engine->lock);
+    engine = stream->sc->engine;
+    silc_snprintf(tuple, sizeof(tuple), "%d%s",
+                 stream->remote_udp->remote_port,
+                 stream->remote_udp->remote_ip);
+    silc_mutex_lock(engine->lock);
+    silc_hash_table_del(engine->udp_remote, tuple);
+    silc_mutex_unlock(engine->lock);
 
     silc_free(stream->remote_udp->remote_ip);
     silc_free(stream->remote_udp);
@@ -914,6 +957,13 @@ void silc_packet_stream_destroy(SilcPacketStream stream)
   silc_free(stream);
 }
 
+/* Return TRUE if the stream is valid */
+
+SilcBool silc_packet_stream_is_valid(SilcPacketStream stream)
+{
+  return stream->destroyed == FALSE;
+}
+
 /* Marks as router stream */
 
 void silc_packet_stream_set_router(SilcPacketStream stream)
@@ -1296,6 +1346,49 @@ SilcBool silc_packet_set_ids(SilcPacketStream stream,
   return TRUE;
 }
 
+/* Return IDs from the packet stream */
+
+SilcBool silc_packet_get_ids(SilcPacketStream stream,
+                            SilcBool *src_id_set, SilcID *src_id,
+                            SilcBool *dst_id_set, SilcID *dst_id)
+{
+  if (src_id && stream->src_id) {
+    (*src_id).type = stream->src_id_type;
+    switch (stream->src_id_type) {
+    case SILC_ID_CLIENT:
+      (*src_id).u.client_id = *(SilcClientID *)stream->src_id;
+      break;
+    case SILC_ID_SERVER:
+      (*src_id).u.server_id = *(SilcServerID *)stream->src_id;
+      break;
+    case SILC_ID_CHANNEL:
+      (*src_id).u.channel_id = *(SilcChannelID *)stream->src_id;
+      break;
+    }
+  }
+  if (stream->src_id && src_id_set)
+    *src_id_set = TRUE;
+
+  if (dst_id && stream->dst_id) {
+    (*dst_id).type = stream->dst_id_type;
+    switch (stream->dst_id_type) {
+    case SILC_ID_CLIENT:
+      (*dst_id).u.client_id = *(SilcClientID *)stream->dst_id;
+      break;
+    case SILC_ID_SERVER:
+      (*dst_id).u.server_id = *(SilcServerID *)stream->dst_id;
+      break;
+    case SILC_ID_CHANNEL:
+      (*dst_id).u.channel_id = *(SilcChannelID *)stream->dst_id;
+      break;
+    }
+  }
+  if (stream->dst_id && dst_id_set)
+    *dst_id_set = TRUE;
+
+  return TRUE;
+}
+
 /* Adds Security ID (SID) */
 
 SilcBool silc_packet_set_sid(SilcPacketStream stream, SilcUInt8 sid)
@@ -1373,12 +1466,15 @@ static inline void silc_packet_send_ctr_increment(SilcPacketStream stream,
                                                  unsigned char *ret_iv)
 {
   unsigned char *iv = silc_cipher_get_iv(cipher);
-  SilcUInt32 pc;
+  SilcUInt32 pc1, pc2;
 
-  /* Increment packet counter */
-  SILC_GET32_MSB(pc, iv + 8);
-  pc++;
-  SILC_PUT32_MSB(pc, iv + 8);
+  /* Increment 64-bit packet counter */
+  SILC_GET32_MSB(pc1, iv + 4);
+  SILC_GET32_MSB(pc2, iv + 8);
+  if (++pc2 == 0)
+    ++pc1;
+  SILC_PUT32_MSB(pc1, iv + 4);
+  SILC_PUT32_MSB(pc2, iv + 8);
 
   /* Reset block counter */
   memset(iv + 12, 0, 4);
@@ -1390,7 +1486,7 @@ static inline void silc_packet_send_ctr_increment(SilcPacketStream stream,
     ret_iv[1] = ret_iv[0] + iv[4];
     ret_iv[2] = ret_iv[0] ^ ret_iv[1];
     ret_iv[3] = ret_iv[0] + ret_iv[2];
-    SILC_PUT32_MSB(pc, ret_iv + 4);
+    SILC_PUT32_MSB(pc2, ret_iv + 4);
     SILC_LOG_HEXDUMP(("IV"), ret_iv, 8);
 
     /* Set new nonce to counter block */
@@ -1461,10 +1557,8 @@ static inline SilcBool silc_packet_send_raw(SilcPacketStream stream,
      type and flags, and calculate correct length.  Private messages with
      private keys and channel messages are special packets as their
      payload is encrypted already. */
-  if ((type == SILC_PACKET_PRIVATE_MESSAGE &&
-       flags & SILC_PACKET_FLAG_PRIVMSG_KEY) ||
-      type == SILC_PACKET_CHANNEL_MESSAGE) {
-
+  if (type == SILC_PACKET_PRIVATE_MESSAGE &&
+      flags & SILC_PACKET_FLAG_PRIVMSG_KEY) {
     /* Padding is calculated from header + IDs */
     if (!ctr)
       SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len +
@@ -1473,8 +1567,26 @@ static inline SilcBool silc_packet_send_raw(SilcPacketStream stream,
     /* Length to encrypt, header + IDs + padding. */
     enclen = (SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len +
              padlen + psnlen);
-  } else {
 
+  } else if (type == SILC_PACKET_CHANNEL_MESSAGE) {
+    if (stream->sc->engine->local_is_router && stream->is_router) {
+      /* Channel messages between routers are encrypted as normal packets.
+        Padding is calculated from true length of the packet. */
+      if (!ctr)
+       SILC_PACKET_PADLEN(truelen + psnlen, block_len, padlen);
+
+      enclen += padlen + psnlen;
+    } else {
+      /* Padding is calculated from header + IDs */
+      if (!ctr)
+       SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len +
+                           psnlen), block_len, padlen);
+
+      /* Length to encrypt, header + IDs + padding. */
+      enclen = (SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len +
+               padlen + psnlen);
+    }
+  } else {
     /* Padding is calculated from true length of the packet */
     if (flags & SILC_PACKET_FLAG_LONG_PAD)
       SILC_PACKET_PADLEN_MAX(truelen + psnlen, block_len, padlen);
@@ -1532,6 +1644,7 @@ static inline SilcBool silc_packet_send_raw(SilcPacketStream stream,
   /* Encrypt the packet */
   if (silc_likely(cipher)) {
     SILC_LOG_DEBUG(("Encrypting packet"));
+    silc_cipher_set_iv(cipher, NULL);
     if (silc_unlikely(!silc_cipher_encrypt(cipher, packet.data + ivlen,
                                           packet.data + ivlen, enclen,
                                           NULL))) {
@@ -1722,16 +1835,19 @@ static inline void silc_packet_receive_ctr_increment(SilcPacketStream stream,
                                                     unsigned char *iv,
                                                     unsigned char *packet_iv)
 {
-  SilcUInt32 pc;
+  SilcUInt32 pc1, pc2;
 
   /* If IV Included flag, set the IV from packet to block counter. */
   if (stream->iv_included) {
     memcpy(iv + 4, packet_iv, 8);
   } else {
-    /* Increment packet counter */
-    SILC_GET32_MSB(pc, iv + 8);
-    pc++;
-    SILC_PUT32_MSB(pc, iv + 8);
+    /* Increment 64-bit packet counter. */
+    SILC_GET32_MSB(pc1, iv + 4);
+    SILC_GET32_MSB(pc2, iv + 8);
+    if (++pc2 == 0)
+      ++pc1;
+    SILC_PUT32_MSB(pc1, iv + 4);
+    SILC_PUT32_MSB(pc2, iv + 8);
   }
 
   /* Reset block counter */
@@ -2049,8 +2165,9 @@ static void silc_packet_read_process(SilcPacketStream stream)
          silc_packet_receive_ctr_increment(stream, iv, NULL);
       }
 
-      silc_cipher_decrypt(cipher, inbuf->data + ivlen, tmp,
-                         block_len, iv);
+      if (silc_cipher_get_mode(cipher) == SILC_CIPHER_MODE_CTR)
+       silc_cipher_set_iv(cipher, NULL);
+      silc_cipher_decrypt(cipher, inbuf->data + ivlen, tmp, block_len, iv);
 
       header = tmp;
       if (stream->iv_included) {
@@ -2307,6 +2424,7 @@ void silc_packet_wait_uninit(void *waiter, SilcPacketStream stream)
   pw->stopped = TRUE;
   silc_cond_broadcast(pw->wait_cond);
   silc_mutex_unlock(pw->wait_lock);
+  silc_thread_yield();
 
   /* Re-acquire lock and free resources */
   silc_mutex_lock(pw->wait_lock);
@@ -2495,7 +2613,7 @@ int silc_packet_wrap_write(SilcStream stream, const unsigned char *data,
   SilcPacketWrapperStream pws = stream;
   SilcBool ret = FALSE;
 
-  /* Call decoder if set */
+  /* Call encoder if set */
   if (pws->coder) {
     silc_buffer_reset(pws->encbuf);
     ret = pws->coder(stream, SILC_STREAM_CAN_WRITE, pws->encbuf,