updates. New data types.
[silc.git] / lib / silccore / silcpacket.c
index 5e07ff205fd39a89d0abbd02376a2b2bf5b6a5d8..9895fa25915b47b38ae7624109eb7eded3097284 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   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
@@ -71,6 +71,13 @@ int silc_packet_write(int sock, SilcBuffer src)
 
 int silc_packet_send(SilcSocketConnection sock, int force_send)
 {
+  SILC_LOG_DEBUG(("Sending packet to %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")));
+
   /* Send now if forced to do so */
   if (force_send == TRUE) {
     int ret;
@@ -102,33 +109,32 @@ int silc_packet_send(SilcSocketConnection sock, int force_send)
    cannot be used. */
 
 void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, 
-                        SilcBuffer buffer, unsigned int len)
+                        SilcBuffer buffer, uint32 len)
 {
   unsigned char mac[32];
-
-  if (cipher) {
-    SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d (%d)", 
-                   cipher->cipher->name, len, len - 2));
-  }
+  uint32 mac_len;
 
   /* Compute HMAC. This assumes that HMAC is created from the entire
      data area thus this uses the length found in buffer, not the length
      sent as argument. */
   if (hmac) {
-    silc_hmac_make(hmac, buffer->data, buffer->len, mac);
-    silc_buffer_put_tail(buffer, mac, hmac->hash->hash->hash_len);
+    silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
+    silc_buffer_put_tail(buffer, mac, mac_len);
     memset(mac, 0, sizeof(mac));
   }
 
   /* Encrypt the data area of the packet. 2 bytes of the packet
      are not encrypted. */
-  if (cipher)
+  if (cipher) {
+    SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d (%d)", 
+                   cipher->cipher->name, len, len - 2));
     cipher->cipher->encrypt(cipher->context, buffer->data + 2, 
                            buffer->data + 2, len - 2, cipher->iv);
+  }
 
   /* Pull the HMAC into the visible data area in the buffer */
   if (hmac)
-    silc_buffer_pull_tail(buffer, hmac->hash->hash->hash_len);
+    silc_buffer_pull_tail(buffer, mac_len);
 }
 
 /* Assembles a new packet to be ready for send out. The buffer sent as
@@ -204,8 +210,7 @@ void silc_packet_assemble(SilcPacketContext *ctx)
 
   /* Get random padding */
 #if 1
-  for (i = 0; i < ctx->padlen; i++)
-    tmppad[i] = silc_rng_get_byte(ctx->rng);
+  for (i = 0; i < ctx->padlen; i++) tmppad[i] = silc_rng_global_get_byte();
 #else
   /* XXX: For testing - to be removed */
   memset(tmppad, 65, sizeof(tmppad));
@@ -238,9 +243,9 @@ void silc_packet_assemble(SilcPacketContext *ctx)
    outgoing buffer in SilcSocketConnection object. */
 
 void silc_packet_send_prepare(SilcSocketConnection sock,
-                             unsigned int header_len,
-                             unsigned int padlen,
-                             unsigned int data_len)
+                             uint32 header_len,
+                             uint32 padlen,
+                             uint32 data_len)
 {
   int totlen, oldlen;
 
@@ -307,11 +312,11 @@ int silc_packet_read(int sock, SilcBuffer dest)
   /* Read the data from the socket. */
   len = read(sock, buf, sizeof(buf));
   if (len < 0) {
-    if (errno == EAGAIN) {
+    if (errno == EAGAIN || errno == EINTR) {
       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
       return -2;
     }
-    SILC_LOG_ERROR(("Cannot read from socket: %d", strerror(errno)));
+    SILC_LOG_ERROR(("Cannot read from socket: %d:%s", sock, strerror(errno)));
     return -1;
   }
 
@@ -358,116 +363,73 @@ int silc_packet_read(int sock, SilcBuffer dest)
    and parsing. If more than one packet was received this calls the
    parser multiple times. The parser callback will get context
    SilcPacketParserContext that includes the packet and the `context'
-   sent to this function. Returns TRUE on success and FALSE on error. */
+   sent to this function. */
 
-int silc_packet_receive_process(SilcSocketConnection sock, 
-                               SilcCipher cipher, SilcHmac hmac,
-                               SilcPacketParserCallback parser, 
-                               void *context)
+void silc_packet_receive_process(SilcSocketConnection sock,
+                                SilcCipher cipher, SilcHmac hmac,
+                                SilcPacketParserCallback parser,
+                                void *context)
 {
   SilcPacketParserContext *parse_ctx;
-  int packetlen, paddedlen, mac_len = 0;
-
-  /* Check whether we received a whole packet. If reading went without
-     errors we either read a whole packet or the read packet is 
-     incorrect and will be dropped. */
-  SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-  if (packetlen < SILC_PACKET_MIN_LEN) {
-    SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-    silc_buffer_clear(sock->inbuf);
-    return FALSE;
-  }
-
-  if (sock->inbuf->len < paddedlen) {
-    /* Two cases: either we haven't read all of the data or this 
-       packet is malformed. Try to read data from the connection.
-       If it fails this packet is malformed. */
-    silc_schedule_with_fd(sock->sock, SILC_TASK_READ, 0, 1);
-    if (silc_packet_receive(sock) < 0) {
-      SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-      silc_buffer_clear(sock->inbuf);
-      return FALSE;
-    }
-  }
+  int packetlen, paddedlen, count, mac_len = 0;
 
-  /* Parse the packets from the data */
-  if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
-    /* Received possibly many packets at once */
-
-    if (hmac)
-      mac_len = hmac->hash->hash->hash_len;
-
-    while (sock->inbuf->len > 0) {
-      SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-
-      if (sock->inbuf->len < paddedlen) {
-       /* Two cases: either we haven't read all of the data or this 
-          packet is malformed. Try to read data from the connection.
-          If it fails this packet is malformed. */
-       silc_schedule_with_fd(sock->sock, SILC_TASK_READ, 0, 1);
-       if (silc_packet_receive(sock) > 0) 
-         continue;
+  /* We need at least 2 bytes of data to be able to start processing
+     the packet. */
+  if (sock->inbuf->len < 2)
+    return;
 
-       SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-       return FALSE;
-      }
+  if (hmac)
+    mac_len = hmac->hmac->len;
 
-      paddedlen += 2;
-      parse_ctx = silc_calloc(1, sizeof(*parse_ctx));
-      parse_ctx->packet = silc_calloc(1, sizeof(*parse_ctx->packet));
-      parse_ctx->packet->buffer = silc_buffer_alloc(paddedlen + mac_len);
-      parse_ctx->sock = sock;
-      parse_ctx->cipher = cipher;
-      parse_ctx->hmac = hmac;
-      parse_ctx->context = context;
-
-      silc_buffer_pull_tail(parse_ctx->packet->buffer, 
-                           SILC_BUFFER_END(parse_ctx->packet->buffer));
-      silc_buffer_put(parse_ctx->packet->buffer, sock->inbuf->data, 
-                     paddedlen + mac_len);
-
-      SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                       parse_ctx->packet->buffer->len),
-                      parse_ctx->packet->buffer->data, 
-                      parse_ctx->packet->buffer->len);
-
-      /* Call the parser */
-      if (parser)
-       (*parser)(parse_ctx);
-
-      /* Pull the packet from inbuf thus we'll get the next one
-        in the inbuf. */
-      silc_buffer_pull(sock->inbuf, paddedlen);
-      if (hmac)
-       silc_buffer_pull(sock->inbuf, mac_len);
+  /* Parse the packets from the data */
+  count = 0;
+  while (sock->inbuf->len > 0) {
+    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
+    paddedlen += 2;
+    count++;
+
+    if (packetlen < SILC_PACKET_MIN_LEN) {
+      SILC_LOG_DEBUG(("Received invalid packet, dropped"));
+      silc_buffer_clear(sock->inbuf);
+      return;
     }
 
-    /* All packets are processed, return successfully. */
-    silc_buffer_clear(sock->inbuf);
-    return TRUE;
-
-  } else {
-    /* Received one packet */
-    
-    SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
-                    sock->inbuf->data, sock->inbuf->len);
+    if (sock->inbuf->len < paddedlen + mac_len) {
+      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest"));
+      return;
+    }
 
     parse_ctx = silc_calloc(1, sizeof(*parse_ctx));
-    parse_ctx->packet = silc_calloc(1, sizeof(*parse_ctx->packet));
-    parse_ctx->packet->buffer = silc_buffer_copy(sock->inbuf);
+    parse_ctx->packet = silc_packet_context_alloc();
+    parse_ctx->packet->buffer = silc_buffer_alloc(paddedlen + mac_len);
     parse_ctx->sock = sock;
     parse_ctx->cipher = cipher;
     parse_ctx->hmac = hmac;
     parse_ctx->context = context;
-    silc_buffer_clear(sock->inbuf);
+
+    silc_buffer_pull_tail(parse_ctx->packet->buffer, 
+                         SILC_BUFFER_END(parse_ctx->packet->buffer));
+    silc_buffer_put(parse_ctx->packet->buffer, sock->inbuf->data, 
+                   paddedlen + mac_len);
+
+    SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
+                     parse_ctx->packet->buffer->len),
+                    parse_ctx->packet->buffer->data, 
+                    parse_ctx->packet->buffer->len);
 
     /* Call the parser */
     if (parser)
       (*parser)(parse_ctx);
 
-    /* Return successfully */
-    return TRUE;
+    /* Pull the packet from inbuf thus we'll get the next one
+       in the inbuf. */
+    silc_buffer_pull(sock->inbuf, paddedlen);
+    if (hmac)
+      silc_buffer_pull(sock->inbuf, mac_len);
   }
+
+  SILC_LOG_DEBUG(("Clearing inbound buffer"));
+  silc_buffer_clear(sock->inbuf);
 }
 
 /* Receives packet from network and reads the data into connection's
@@ -481,18 +443,20 @@ int silc_packet_receive(SilcSocketConnection sock)
 {
   int ret;
 
+  SILC_LOG_DEBUG(("Receiving packet from %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")));
+
   /* Allocate the incoming data buffer if not done already. */
   if (!sock->inbuf)
     sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
-  
+
   /* Read some data from connection */
   ret = silc_packet_read(sock->sock, sock->inbuf);
 
-  /* Error */
-  if (ret == -1) {
-    SILC_LOG_ERROR(("Error reading packet, dropped"));
-  }
-
   return ret;
 }
 
@@ -504,15 +468,16 @@ static int silc_packet_check_mac(SilcHmac hmac, SilcBuffer buffer)
   /* Check MAC */
   if (hmac) {
     unsigned char mac[32];
+    uint32 mac_len;
     
     SILC_LOG_DEBUG(("Verifying MAC"));
 
     /* Compute HMAC of packet */
     memset(mac, 0, sizeof(mac));
-    silc_hmac_make(hmac, buffer->data, buffer->len, mac);
+    silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
 
     /* Compare the HMAC's (buffer->tail has the packet's HMAC) */
-    if (memcmp(mac, buffer->tail, hmac->hash->hash->hash_len)) {
+    if (memcmp(mac, buffer->tail, mac_len)) {
       SILC_LOG_DEBUG(("MAC failed"));
       return FALSE;
     }
@@ -536,8 +501,8 @@ static int silc_packet_decrypt_rest(SilcCipher cipher, SilcHmac hmac,
 
     /* Pull MAC from packet before decryption */
     if (hmac) {
-      if ((buffer->len - hmac->hash->hash->hash_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, hmac->hash->hash->hash_len);
+      if ((buffer->len - hmac->hmac->len) > SILC_PACKET_MIN_LEN) {
+       silc_buffer_push_tail(buffer, hmac->hmac->len);
       } else {
        SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
        return FALSE;
@@ -572,12 +537,12 @@ static int silc_packet_decrypt_rest_special(SilcCipher cipher,
 {
   /* Decrypt rest of the header plus padding */
   if (cipher) {
-    unsigned short truelen, len1, len2, padlen;
+    uint16 truelen, len1, len2, padlen;
 
     /* Pull MAC from packet before decryption */
     if (hmac) {
-      if ((buffer->len - hmac->hash->hash->hash_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, hmac->hash->hash->hash_len);
+      if ((buffer->len - hmac->hmac->len) > SILC_PACKET_MIN_LEN) {
+       silc_buffer_push_tail(buffer, hmac->hmac->len);
       } else {
        SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
        return FALSE;
@@ -609,28 +574,33 @@ static int silc_packet_decrypt_rest_special(SilcCipher cipher,
    the HMAC of the packet. If any other special or customized decryption
    processing is required this function cannot be used. This returns
    -1 on error, 0 when packet is normal packet and 1 when the packet
-   is special and requires special processing. */
+   is special and requires special processing. 
+
+   The `check_packet' is a callback funtion that this function will 
+   call.  The callback relates to the checking whether the packet is
+   normal packet or special packet and how it should be processed.  If
+   the callback return TRUE the packet is normal and FALSE if the packet
+   is special and requires special procesing. */
 
 int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
-                       SilcBuffer buffer, SilcPacketContext *packet)
+                       SilcBuffer buffer, SilcPacketContext *packet,
+                       SilcPacketCheckDecrypt check_packet,
+                       void *context)
 {
-#if 0
-  SILC_LOG_DEBUG(("Decrypting packet, cipher %s, len %d (%d)", 
-                 cipher->cipher->name, len, len - 2));
-#endif
+  int check;
 
   /* Decrypt start of the packet header */
   if (cipher)
-    cipher->cipher->decrypt(cipher->context, buffer->data + 2,
-                           buffer->data + 2, SILC_PACKET_MIN_HEADER_LEN - 2,
-                           cipher->iv);
+    silc_cipher_decrypt(cipher, buffer->data + 2, buffer->data + 2, 
+                       SILC_PACKET_MIN_HEADER_LEN - 2, cipher->iv);
+
+  /* Do packet checking, whether the packet is normal or special */ 
+  check = check_packet((SilcPacketType)buffer->data[3], buffer,
+                      packet, context);
 
   /* If the packet type is not any special type lets decrypt rest
      of the packet here. */
-  if ((buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-      !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) ||
-      buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE) {
-
+  if (check == TRUE) {
     /* Normal packet, decrypt rest of the packet */
     if (!silc_packet_decrypt_rest(cipher, hmac, buffer))
       return -1;
@@ -643,7 +613,8 @@ int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
   } else {
     /* Packet requires special handling, decrypt rest of the header.
        This only decrypts. */
-    silc_packet_decrypt_rest_special(cipher, hmac, buffer);
+    if (!silc_packet_decrypt_rest_special(cipher, hmac, buffer))
+      return -1;
 
     /* Check MAC */
     if (!silc_packet_check_mac(hmac, buffer))
@@ -662,7 +633,7 @@ int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
 SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
 {
   SilcBuffer buffer = ctx->buffer;
-  int len;
+  int len, ret;
 
   SILC_LOG_DEBUG(("Parsing incoming packet"));
 
@@ -681,6 +652,8 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
                             SILC_STR_UI_SHORT(&ctx->dst_id_len),
                             SILC_STR_UI_CHAR(&ctx->src_id_type),
                             SILC_STR_END);
+  if (len == -1)
+    return SILC_PACKET_NONE;
 
   if (ctx->src_id_len > SILC_PACKET_MAX_ID_LEN ||
       ctx->dst_id_len > SILC_PACKET_MAX_ID_LEN) {
@@ -692,14 +665,17 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
   ctx->padlen = SILC_PACKET_PADLEN(ctx->truelen);
 
   silc_buffer_pull(buffer, len);
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
-                                                 ctx->src_id_len),
-                      SILC_STR_UI_CHAR(&ctx->dst_id_type),
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                 ctx->dst_id_len),
-                      SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
-                      SILC_STR_END);
+  ret = silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
+                                                       ctx->src_id_len),
+                            SILC_STR_UI_CHAR(&ctx->dst_id_type),
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
+                                                       ctx->dst_id_len),
+                            SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
+                            SILC_STR_END);
+  if (ret == -1)
+    return SILC_PACKET_NONE;
+
   silc_buffer_push(buffer, len);
 
   SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len), 
@@ -723,7 +699,7 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
 SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
 {
   SilcBuffer buffer = ctx->buffer;
-  int len, tmplen;
+  int len, tmplen, ret;
 
   SILC_LOG_DEBUG(("Parsing incoming packet"));
 
@@ -742,6 +718,8 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
                             SILC_STR_UI_SHORT(&ctx->dst_id_len),
                             SILC_STR_UI_CHAR(&ctx->src_id_type),
                             SILC_STR_END);
+  if (len == -1)
+    return SILC_PACKET_NONE;
 
   if (ctx->src_id_len > SILC_PACKET_MAX_ID_LEN ||
       ctx->dst_id_len > SILC_PACKET_MAX_ID_LEN) {
@@ -756,14 +734,17 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
   ctx->padlen = SILC_PACKET_PADLEN(tmplen);
 
   silc_buffer_pull(buffer, len);
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
-                                                 ctx->src_id_len),
-                      SILC_STR_UI_CHAR(&ctx->dst_id_type),
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                 ctx->dst_id_len),
-                      SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
-                      SILC_STR_END);
+  ret = silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
+                                                       ctx->src_id_len),
+                            SILC_STR_UI_CHAR(&ctx->dst_id_type),
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
+                                                       ctx->dst_id_len),
+                            SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
+                            SILC_STR_END);
+  if (ret == -1)
+    return SILC_PACKET_NONE;
+
   silc_buffer_push(buffer, len);
 
   SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len), 
@@ -777,3 +758,42 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
 
   return ctx->type;
 }
+
+/* Allocate packet context */
+
+SilcPacketContext *silc_packet_context_alloc()
+{
+  SilcPacketContext *ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->users++;
+  return ctx;
+}
+
+/* Increse the reference count of the packet context. */
+
+SilcPacketContext *silc_packet_context_dup(SilcPacketContext *ctx)
+{
+  ctx->users++;
+  SILC_LOG_DEBUG(("Packet context %p refcnt %d->%d", ctx, ctx->users - 1,
+                 ctx->users));
+  return ctx;
+}
+
+/* Decrese the reference count of the packet context and free it only if
+   it is zero. */
+
+void silc_packet_context_free(SilcPacketContext *ctx)
+{
+  ctx->users--;
+  SILC_LOG_DEBUG(("Packet context %p refcnt %d->%d", ctx, ctx->users + 1,
+                 ctx->users));
+  if (ctx->users < 1)
+    {
+      if (ctx->buffer)
+       silc_buffer_free(ctx->buffer);
+      if (ctx->src_id)
+       silc_free(ctx->src_id);
+      if (ctx->dst_id)
+       silc_free(ctx->dst_id);
+      silc_free(ctx);
+    }
+}