Merged from silc_1_0_branch.
[silc.git] / lib / silccore / silcpacket.c
index b19f2a9d70278a446c77c713f555976cdb4df18b..7bac3681106e2d8282b224cda531b985a7b9d7d8 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silcpacket.c
+  silcpacket.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   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
-  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
 
 ******************************************************************************/
 
-/* Writes data from encrypted buffer to the socket connection. If the
-   data cannot be written at once, it will be written later with a timeout. 
-   The data is written from the data section of the buffer, not from head
-   or tail section. This automatically pulls the data section towards end
-   after writing the data. */
-
-int silc_packet_write(int sock, SilcBuffer src)
-{
-  int ret = 0;
-
-  SILC_LOG_DEBUG(("Writing data to socket %d", sock));
-
-  if (src->len > 0) {
-    ret = write(sock, src->data, src->len);
-    if (ret < 0) {
-      if (errno == EAGAIN) {
-       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
-       return -2;
-      }
-      SILC_LOG_ERROR(("Cannot write to socket: %s", strerror(errno)));
-      return -1;
-    }
-
-    silc_buffer_pull(src, ret);
-  }
-
-  SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
-
-  return ret;
-}
-
 /* Actually sends the packet. This flushes the connections outgoing data
    buffer. If data is sent directly to the network this returns the bytes
    written, if error occured this returns -1 and if the data could not
@@ -69,7 +37,7 @@ int silc_packet_write(int sock, SilcBuffer src)
    later time. If `force_send' is TRUE this attempts to write the data
    directly to the network, if FALSE, this returns -2. */
 
-int silc_packet_send(SilcSocketConnection sock, int force_send)
+int silc_packet_send(SilcSocketConnection sock, bool force_send)
 {
   SILC_LOG_DEBUG(("Sending packet to %s:%d [%s]", sock->hostname,
                  sock->port,  
@@ -85,10 +53,11 @@ int silc_packet_send(SilcSocketConnection sock, int force_send)
     SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
 
     /* Write to network */
-    ret = silc_packet_write(sock->sock, sock->outbuf);
+    ret = silc_socket_write(sock);
 
     if (ret == -1) {
-      SILC_LOG_ERROR(("Error sending packet, dropped"));
+      SILC_LOG_ERROR(("Error sending packet, dropped: %s", 
+                      strerror(errno)));
     }
     if (ret != -2)
       return ret;
@@ -108,178 +77,172 @@ int silc_packet_send(SilcSocketConnection sock, int force_send)
    other process of HMAC computing and encryption is needed this function
    cannot be used. */
 
-void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, 
-                        SilcBuffer buffer, uint32 len)
+void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, SilcUInt32 sequence,
+                        SilcBuffer buffer, SilcUInt32 len)
 {
-  unsigned char mac[32];
-  uint32 mac_len;
 
-  /* Compute HMAC. This assumes that HMAC is created from the entire
+  /* Encrypt the data area of the packet. */
+  if (cipher) {
+    SILC_LOG_DEBUG(("Encrypting packet (%d), cipher %s, len %d", 
+                   sequence, silc_cipher_get_name(cipher), len));
+    silc_cipher_encrypt(cipher, buffer->data, buffer->data, len, NULL);
+  }
+
+  /* Compute HMAC. This assumes that MAC is computed 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, &mac_len);
-    silc_buffer_put_tail(buffer, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-  }
+    unsigned char mac[32], psn[4];
+    SilcUInt32 mac_len;
 
-  /* Encrypt the data area of the packet. 2 bytes of the packet
-     are not encrypted. */
-  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);
-  }
+    silc_hmac_init(hmac);
+    SILC_PUT32_MSB(sequence, psn);
+    silc_hmac_update(hmac, psn, 4);
+    silc_hmac_update(hmac, buffer->data, buffer->len);
+    silc_hmac_final(hmac, mac, &mac_len);
 
-  /* Pull the HMAC into the visible data area in the buffer */
-  if (hmac)
+    /* Put MAC and pull the it into the visible data area in the buffer */
+    silc_buffer_put_tail(buffer, mac, mac_len);
     silc_buffer_pull_tail(buffer, mac_len);
+  }
 }
 
-/* Assembles a new packet to be ready for send out. The buffer sent as
-   argument must include the data to be sent and it must not be encrypted. 
-   The packet also must have enough free space so that the SILC header
-   and padding maybe added to the packet. The packet is encrypted after 
-   this function has returned.
-
-   The buffer sent as argument should be something like following:
-
-   --------------------------------------------
-   | head             | data           | tail |
-   --------------------------------------------
-   ^                  ^
-   58 bytes           x bytes
-
-   So that the SILC header and 1 - 16 bytes of padding can fit to
-   the buffer. After assembly the buffer might look like this:
-
-   --------------------------------------------
-   | data                              |      |
-   --------------------------------------------
-   ^                                   ^
-   Start of assembled packet
-
-   Packet construct is as follows (* = won't be encrypted):
+/* Assembles a new packet to be ready for send out. */
 
-   n bytes       SILC Header
-      2 bytes     Payload length  (*)
-      1 byte      Flags
-      1 byte      Packet type
-      2 bytes     Source ID Length
-      2 bytes     Destination ID Length
-      1 byte      Source ID Type
-      n bytes     Source ID
-      1 byte      Destination ID Type
-      n bytes     Destination ID
+bool silc_packet_assemble(SilcPacketContext *packet, SilcRng rng,
+                         SilcCipher cipher, SilcHmac hmac,
+                         SilcSocketConnection sock,
+                         const unsigned char *data, SilcUInt32 data_len,
+                         const SilcBuffer assembled_packet)
+{ 
+  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];   
+  unsigned int block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+  int i, ret;
 
-   1 - 16 bytes    Padding
-
-   n bytes        Data payload
-
-   All fields in the packet will be authenticated by MAC. The MAC is
-   not computed here, it must be computed separately before encrypting
-   the packet.
-
-*/
+  SILC_LOG_DEBUG(("Assembling outgoing packet"));
 
-void silc_packet_assemble(SilcPacketContext *ctx)
-{
-  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
-  int i;
+  /* Calculate the packet's length and padding length if upper layer
+     didn't already do it. */
 
-  SILC_LOG_DEBUG(("Assembling outgoing packet"));
-  
   /* Get the true length of the packet. This is saved as payload length
      into the packet header. This does not include the length of the
      padding. */
-  if (!ctx->truelen)
-    ctx->truelen = ctx->buffer->len + SILC_PACKET_HEADER_LEN + 
-      ctx->src_id_len + ctx->dst_id_len;
+  if (!packet->truelen) {
+    data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
+                                  packet->src_id_len + packet->dst_id_len);
+    packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
+      packet->src_id_len + packet->dst_id_len;
+  }
 
   /* Calculate the length of the padding. The padding is calculated from
-     the data that will be encrypted. As protocol states 3 first bytes
-     of the packet are not encrypted they are not included in the
-     padding calculation. */
-  if (!ctx->padlen)
-    ctx->padlen = SILC_PACKET_PADLEN(ctx->truelen);
-
-  /* Put the start of the data section to the right place. */
-  silc_buffer_push(ctx->buffer, SILC_PACKET_HEADER_LEN + 
-                  ctx->src_id_len + ctx->dst_id_len + ctx->padlen);
+     the data that will be encrypted. */
+  if (!packet->padlen) {
+    if (packet->long_pad)
+      SILC_PACKET_PADLEN_MAX(packet->truelen, block_len, packet->padlen);
+    else
+      SILC_PACKET_PADLEN(packet->truelen, block_len, packet->padlen);
+  }
+
+  /* Now prepare the outgoing data buffer for packet sending and start
+     assembling the packet. */
+
+  /* Return pointer to the assembled packet */
+  if (!silc_packet_send_prepare(sock, packet->truelen - data_len,
+                               packet->padlen, data_len, hmac,
+                               assembled_packet))
+    return FALSE;
 
   /* Get random padding */
-#if 1
-  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));
-#endif
-
-  /* Create the packet. This creates the SILC header and adds padding,
-     rest of the buffer remains as it is. */
-  silc_buffer_format(ctx->buffer, 
-                    SILC_STR_UI_SHORT(ctx->truelen),
-                    SILC_STR_UI_CHAR(ctx->flags),
-                    SILC_STR_UI_CHAR(ctx->type),
-                    SILC_STR_UI_SHORT(ctx->src_id_len),
-                    SILC_STR_UI_SHORT(ctx->dst_id_len),
-                    SILC_STR_UI_CHAR(ctx->src_id_type),
-                    SILC_STR_UI_XNSTRING(ctx->src_id, ctx->src_id_len),
-                    SILC_STR_UI_CHAR(ctx->dst_id_type),
-                    SILC_STR_UI_XNSTRING(ctx->dst_id, ctx->dst_id_len),
-                    SILC_STR_UI_XNSTRING(tmppad, ctx->padlen),
-                    SILC_STR_END);
-
-  SILC_LOG_HEXDUMP(("Assembled packet, len %d", ctx->buffer->len), 
-                  ctx->buffer->data, ctx->buffer->len);
+  if (rng)
+    for (i = 0; i < packet->padlen; i++) tmppad[i] =
+                                          silc_rng_get_byte_fast(rng);
+  else
+    for (i = 0; i < packet->padlen; i++) tmppad[i] =
+                                          silc_rng_global_get_byte_fast();
+
+  /* Create the packet. This creates the SILC header, adds padding, and
+     the actual packet data. */
+  ret =
+    silc_buffer_format(assembled_packet,
+                      SILC_STR_UI_SHORT(packet->truelen),
+                      SILC_STR_UI_CHAR(packet->flags),
+                      SILC_STR_UI_CHAR(packet->type),
+                      SILC_STR_UI_CHAR(packet->padlen),
+                      SILC_STR_UI_CHAR(0),
+                      SILC_STR_UI_CHAR(packet->src_id_len),
+                      SILC_STR_UI_CHAR(packet->dst_id_len),
+                      SILC_STR_UI_CHAR(packet->src_id_type),
+                      SILC_STR_UI_XNSTRING(packet->src_id,
+                                           packet->src_id_len),
+                      SILC_STR_UI_CHAR(packet->dst_id_type),
+                      SILC_STR_UI_XNSTRING(packet->dst_id,
+                                           packet->dst_id_len),
+                      SILC_STR_UI_XNSTRING(tmppad, packet->padlen),
+                      SILC_STR_UI_XNSTRING(data, data_len),
+                      SILC_STR_END);
+  if (ret < 0)
+    return FALSE;
+
+  SILC_LOG_HEXDUMP(("Assembled packet, len %d", assembled_packet->len),
+                  assembled_packet->data, assembled_packet->len);
 
-  SILC_LOG_DEBUG(("Outgoing packet assembled"));
+  return TRUE;
 }
 
 /* Prepare outgoing data buffer for packet sending. This moves the data
    area so that new packet may be added into it. If needed this allocates
    more space to the buffer. This handles directly the connection's
-   outgoing buffer in SilcSocketConnection object. */
+   outgoing buffer in SilcSocketConnection object, and returns the
+   pointer to that buffer into the `packet'. */
 
-void silc_packet_send_prepare(SilcSocketConnection sock,
-                             uint32 header_len,
-                             uint32 padlen,
-                             uint32 data_len)
-{
-  int totlen, oldlen;
+bool silc_packet_send_prepare(SilcSocketConnection sock,
+                             SilcUInt32 header_len,
+                             SilcUInt32 pad_len,
+                             SilcUInt32 data_len,
+                             SilcHmac hmac,
+                             const SilcBuffer packet)
+{ 
+  SilcUInt32 totlen;
+  unsigned char *oldptr;
+  unsigned int mac_len = hmac ? silc_hmac_len(hmac) : 0;
+
+  if (!packet)
+    return FALSE;
 
-  totlen = header_len + padlen + data_len;
+  totlen = header_len + pad_len + data_len;
 
   /* Prepare the outgoing buffer for packet sending. */
   if (!sock->outbuf) {
     /* Allocate new buffer. This is done only once per connection. */
     SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
-    
-    sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
-    silc_buffer_pull_tail(sock->outbuf, totlen);
-    silc_buffer_pull(sock->outbuf, header_len + padlen);
-  } else {
-    if (SILC_IS_OUTBUF_PENDING(sock)) {
-      /* There is some pending data in the buffer. */
-
-      /* Allocate more space if needed */
-      if ((sock->outbuf->end - sock->outbuf->tail) < data_len) {
-       SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
-       sock->outbuf = silc_buffer_realloc(sock->outbuf, 
-                                          sock->outbuf->truelen + totlen);
-      }
 
-      oldlen = sock->outbuf->len;
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
-    } else {
+    sock->outbuf = silc_buffer_alloc(totlen > SILC_PACKET_DEFAULT_SIZE ?
+                                    totlen : SILC_PACKET_DEFAULT_SIZE);
+    if (!sock->outbuf)
+      return FALSE;
+  } else {
+    if (!SILC_IS_OUTBUF_PENDING(sock)) {
       /* Buffer is free for use */
       silc_buffer_clear(sock->outbuf);
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen);
     }
   }
+
+  /* Allocate more space if needed */
+  if ((sock->outbuf->end - sock->outbuf->tail) < (totlen + mac_len)) {
+    SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
+    sock->outbuf = silc_buffer_realloc(sock->outbuf,
+                                      sock->outbuf->truelen + (totlen * 2));
+    if (!sock->outbuf)
+      return FALSE;
+  }
+
+  /* Pull data area for the new packet, and return pointer to the start of
+     the data area and save the pointer in to the `packet'. */
+  oldptr = silc_buffer_pull_tail(sock->outbuf, totlen + mac_len);
+  silc_buffer_set(packet, oldptr, totlen + mac_len);
+  silc_buffer_push_tail(packet, mac_len);
+
+  return TRUE;
 }
 
 /******************************************************************************
@@ -288,335 +251,295 @@ void silc_packet_send_prepare(SilcSocketConnection sock,
 
 ******************************************************************************/
 
-/* Reads data from the socket connection into the incoming data buffer.
-   However, this does not parse the packet, it only reads some amount from
-   the network. If there are more data available that can be read at a time
-   the rest of the data will be read later with a timeout and only after
-   that the packet is ready to be parsed. 
-
-   The destination buffer sent as argument must be initialized before 
-   calling this function, and, the data section and the start of the tail
-   section must be same. Ie. we add the read data to the tail section of
-   the buffer hence the data section is the start of the buffer.
+static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, 
+                              SilcUInt32 sequence, SilcBuffer buffer, 
+                              bool normal);
+static bool silc_packet_check_mac(SilcHmac hmac,
+                                 const unsigned char *data,
+                                 SilcUInt32 data_len,
+                                 const unsigned char *packet_mac,
+                                 SilcUInt32 sequence);
 
-   This returns amount of bytes read or -1 on error or -2 on case where
-   all of the data could not be read at once. */
+/* Receives packet from network and reads the data into connection's
+   incoming data buffer. If the data was read directly this returns the
+   read bytes, if error occured this returns -1, if the data could not
+   be read directly at this time this returns -2 in which case the data
+   should be read again at some later time, or If EOF occured this returns
+   0. */
 
-int silc_packet_read(int sock, SilcBuffer dest)
+int silc_packet_receive(SilcSocketConnection sock)
 {
-  int len = 0;
-  unsigned char buf[SILC_PACKET_READ_SIZE];
-
-  SILC_LOG_DEBUG(("Reading data from socket %d", sock));
-
-  /* Read the data from the socket. */
-  len = read(sock, buf, sizeof(buf));
-  if (len < 0) {
-    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:%s", sock, strerror(errno)));
-    return -1;
-  }
-
-  if (!len)
-    return 0;
-
-  /* Insert the data to the buffer. If the data doesn't fit to the 
-     buffer space is allocated for the buffer. */
-  /* XXX: This may actually be bad thing as if there is pending data in
-     the buffer they will be lost! */
-  if (dest) {
-
-    /* If the data doesn't fit we just have to allocate a whole new 
-       data area */
-    if (dest->truelen <= len) {
-
-      /* Free the old buffer */
-      memset(dest->head, 'F', dest->truelen);
-      silc_free(dest->head);
-
-      /* Allocate new data area */
-      len += SILC_PACKET_DEFAULT_SIZE;
-      dest->data = silc_calloc(len, sizeof(char));
-      dest->truelen = len;
-      dest->len = 0;
-      dest->head = dest->data;
-      dest->data = dest->data;
-      dest->tail = dest->data;
-      dest->end = dest->data + dest->truelen;
-      len -= SILC_PACKET_DEFAULT_SIZE;
-    }
+  int ret;
 
-    silc_buffer_put_tail(dest, buf, len);
-    silc_buffer_pull_tail(dest, len);
-  }
+  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")));
 
-  SILC_LOG_DEBUG(("Read %d bytes", len));
+  /* Read some data from connection */
+  ret = silc_socket_read(sock);
 
-  return len;
+  return ret;
 }
 
-/* Processes the received data. This checks the received data and 
-   calls parser callback that handles the actual packet decryption
-   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. */
-
-void silc_packet_receive_process(SilcSocketConnection sock,
+/* Processes and decrypts the incmoing data, and calls parser callback
+   for each received packet that will handle the actual packet 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 `parser_context' sent to this
+   function. 
+   
+   The `local_is_router' indicates whether the caller is router server
+   in which case the receiving process of a certain packet types may
+   be special.  Normal server and client must set it to FALSE.  The
+   SilcPacketParserContext will indicate also whether the received
+   packet was normal or special packet. */
+
+bool silc_packet_receive_process(SilcSocketConnection sock,
+                                bool local_is_router,
                                 SilcCipher cipher, SilcHmac hmac,
+                                SilcUInt32 sequence,
                                 SilcPacketParserCallback parser,
-                                void *context)
+                                void *parser_context)
 {
   SilcPacketParserContext *parse_ctx;
-  int packetlen, paddedlen, count, mac_len = 0;
+  SilcUInt16 packetlen;
+  SilcUInt32 paddedlen, mac_len = 0, block_len;
+  int ret;
+  bool cont = TRUE;
+  unsigned char tmp[SILC_PACKET_MIN_HEADER_LEN], *header;
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
 
-  /* We need at least 2 bytes of data to be able to start processing
-     the packet. */
-  if (sock->inbuf->len < 2)
-    return;
+  /* Do not process for disconnected connection */
+  if (SILC_IS_DISCONNECTED(sock))
+    return TRUE;
+
+  if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN)
+    return TRUE;
 
   if (hmac)
-    mac_len = hmac->hmac->len;
+    mac_len = silc_hmac_len(hmac);
 
   /* Parse the packets from the data */
-  count = 0;
-  while (sock->inbuf->len > 0) {
-    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-    paddedlen += 2;
-    count++;
+  while (sock->inbuf->len > 0 && cont) {
+
+    if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN) {
+      SILC_LOG_DEBUG(("Partial packet in queue, waiting for the rest"));
+      return TRUE;
+    }
 
+    /* Decrypt first block of the packet to get the length field out */
+    if (cipher) {
+      block_len = silc_cipher_get_block_len(cipher);
+      memcpy(iv, silc_cipher_get_iv(cipher), block_len);
+      silc_cipher_decrypt(cipher, sock->inbuf->data, tmp, block_len, iv);
+      header = tmp;
+    } else {
+      block_len = SILC_PACKET_MIN_HEADER_LEN;
+      header = sock->inbuf->data;
+    }
+
+    /* Get packet lenght and full packet length with padding */
+    SILC_PACKET_LENGTH(header, packetlen, paddedlen);
+
+    /* Sanity checks */
     if (packetlen < SILC_PACKET_MIN_LEN) {
-      SILC_LOG_DEBUG(("Received invalid packet, dropped"));
+      SILC_LOG_ERROR(("Received too short packet"));
+      memset(header, 0, sizeof(header));
       silc_buffer_clear(sock->inbuf);
-      return;
+      return FALSE;
     }
 
     if (sock->inbuf->len < paddedlen + mac_len) {
-      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest"));
-      return;
+      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest "
+                     "(%d bytes)", paddedlen + mac_len - sock->inbuf->len));
+      SILC_SET_INBUF_PENDING(sock);
+      memset(tmp, 0, sizeof(tmp));
+      return TRUE;
     }
 
+    /* Check MAC of the packet */
+    if (!silc_packet_check_mac(hmac, sock->inbuf->data, paddedlen,
+                              sock->inbuf->data + paddedlen, sequence)) {
+      SILC_LOG_WARNING(("Packet MAC check failed %s:%d "
+                       "[%s type %d len %dB seq %d] [%s]",
+                       sock->hostname, sock->port,
+                       silc_get_packet_name(header[3]),
+                       header[3], paddedlen, sequence,
+                       (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                        sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                        sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                        "Router")));
+      memset(tmp, 0, sizeof(tmp));
+      silc_buffer_clear(sock->inbuf);
+      return FALSE;
+    }
+
+    SILC_UNSET_INBUF_PENDING(sock);
     parse_ctx = silc_calloc(1, sizeof(*parse_ctx));
+    if (!parse_ctx)
+      return FALSE;
     parse_ctx->packet = silc_packet_context_alloc();
-    parse_ctx->packet->buffer = silc_buffer_alloc(paddedlen + mac_len);
+    parse_ctx->packet->buffer = silc_buffer_alloc_size(paddedlen);
+    parse_ctx->packet->type = (SilcPacketType)header[3];
+    parse_ctx->packet->padlen = (SilcUInt8)header[4];
+    parse_ctx->packet->sequence = sequence++;
     parse_ctx->sock = sock;
-    parse_ctx->context = context;
+    parse_ctx->context = parser_context;
+
+    /* Check whether this is normal or special packet */
+    if (local_is_router) {
+      if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+         (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+       parse_ctx->normal = FALSE;
+      else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE || 
+              (header[3] == SILC_PACKET_CHANNEL_MESSAGE &&
+               sock->type == SILC_SOCKET_TYPE_ROUTER))
+       parse_ctx->normal = TRUE;
+    } else {
+      if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+         (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+       parse_ctx->normal = FALSE;
+      else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE)
+       parse_ctx->normal = TRUE;
+    }
 
-    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 (%d) len %d",
+                     sequence - 1, paddedlen + mac_len),
+                    sock->inbuf->data, paddedlen + mac_len);
+
+    /* Put the decrypted part, and rest of the encrypted data, and decrypt */
+    silc_buffer_put(parse_ctx->packet->buffer, header, block_len);
+    silc_buffer_pull(parse_ctx->packet->buffer, block_len);
+    silc_buffer_put(parse_ctx->packet->buffer, sock->inbuf->data + block_len,
+                   paddedlen - block_len);
+    if (cipher) {
+      silc_cipher_set_iv(cipher, iv);
+      ret = silc_packet_decrypt(cipher, hmac, parse_ctx->packet->sequence, 
+                               parse_ctx->packet->buffer, 
+                               parse_ctx->normal);
+      if (ret < 0) {
+       SILC_LOG_WARNING(("Packet decryption failed %s:%d [%s] [%s]", 
+                         sock->hostname, sock->port,
+                         silc_get_packet_name(parse_ctx->packet->type),
+                         (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                          sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                          sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                          "Router")));
+       memset(tmp, 0, sizeof(tmp));
+       silc_packet_context_free(parse_ctx->packet);
+       silc_free(parse_ctx);
+       return FALSE;
+      }
+    }
+    silc_buffer_push(parse_ctx->packet->buffer, block_len);
 
-    SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                     parse_ctx->packet->buffer->len),
-                    parse_ctx->packet->buffer->data, 
+    SILC_LOG_HEXDUMP(("Fully decrypted 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);
+    silc_buffer_pull(sock->inbuf, paddedlen + mac_len);
+
+    /* Call the parser */
+    cont = (*parser)(parse_ctx, parser_context);
+    memset(tmp, 0, sizeof(tmp));
   }
 
+  if (cont == FALSE && sock->inbuf->len > 0)
+    return TRUE;
+
   SILC_LOG_DEBUG(("Clearing inbound buffer"));
   silc_buffer_clear(sock->inbuf);
+  return TRUE;
 }
 
-/* Receives packet from network and reads the data into connection's
-   incoming data buffer. If the data was read directly this returns the
-   read bytes, if error occured this returns -1, if the data could not
-   be read directly at this time this returns -2 in which case the data
-   should be read again at some later time, or If EOF occured this returns
-   0. */
-
-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);
-
-  return ret;
-}
-
-/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
-   after packet has been totally decrypted and parsed. */
+/* Checks MAC in the packet. Returns TRUE if MAC is Ok. */
 
-static int silc_packet_check_mac(SilcHmac hmac, SilcBuffer buffer)
+static bool silc_packet_check_mac(SilcHmac hmac,
+                                 const unsigned char *data,
+                                 SilcUInt32 data_len,
+                                 const unsigned char *packet_mac,
+                                 SilcUInt32 sequence)
 {
   /* Check MAC */
   if (hmac) {
-    unsigned char mac[32];
-    uint32 mac_len;
+    unsigned char mac[32], psn[4];
+    SilcUInt32 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, &mac_len);
-
-    /* Compare the HMAC's (buffer->tail has the packet's HMAC) */
-    if (memcmp(mac, buffer->tail, mac_len)) {
+    silc_hmac_init(hmac);
+    SILC_PUT32_MSB(sequence, psn);
+    silc_hmac_update(hmac, psn, 4);
+    silc_hmac_update(hmac, data, data_len);
+    silc_hmac_final(hmac, mac, &mac_len);
+
+    /* Compare the MAC's */
+    if (memcmp(packet_mac, mac, mac_len)) {
       SILC_LOG_ERROR(("MAC failed"));
       return FALSE;
     }
     
     SILC_LOG_DEBUG(("MAC is Ok"));
-    memset(mac, 0, sizeof(mac));
   }
   
   return TRUE;
 }
 
-/* Decrypts rest of the packet (after decrypting just the SILC header).
-   After calling this function the packet is ready to be parsed by calling 
-   silc_packet_parse. If everything goes without errors this returns TRUE,
-   if packet is malformed this returns FALSE. */
+/* Decrypts SILC packet.  Handles both normal and special packet decryption.
+   Return 0 when packet is normal and 1 when it it special, -1 on error. */
 
-static int silc_packet_decrypt_rest(SilcCipher cipher, SilcHmac hmac,
-                                   SilcBuffer buffer)
+static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
+                              SilcUInt32 sequence, SilcBuffer buffer, 
+                              bool normal)
 {
-  if (cipher) {
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      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;
+  /* If the packet type is not any special type lets decrypt rest
+     of the packet here. */
+  if (normal == TRUE) {
+    if (cipher) {
+      /* Decrypt rest of the packet */
+      SILC_LOG_DEBUG(("Decrypting the packet"));
+      if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data,
+                              buffer->len, NULL)) {
+       SILC_LOG_ERROR(("silc_cipher_decrypt failed"));
+       return -1;
       }
     }
+    return 0;
 
-    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
-
-    /* Decrypt rest of the packet */
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    cipher->cipher->decrypt(cipher->context, buffer->data + 2,
-                           buffer->data + 2, buffer->len - 2,
-                           cipher->iv);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
-                    buffer->data, buffer->len);
-  }
-
-  return TRUE;
-}
-
-/* Decrypts rest of the SILC Packet header that has been decrypted partly
-   already. This decrypts the padding of the packet also. After calling 
-   this function the packet is ready to be parsed by calling function 
-   silc_packet_parse. This is used in special packet reception (protocol
-   defines the way of decrypting special packets). */
-
-static int silc_packet_decrypt_rest_special(SilcCipher cipher,
-                                           SilcHmac hmac,
-                                           SilcBuffer buffer)
-{
-  /* Decrypt rest of the header plus padding */
-  if (cipher) {
-    uint16 truelen, len1, len2, padlen;
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      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;
+  } else {
+    /* Decrypt rest of the header plus padding */
+    if (cipher) {
+      SilcUInt16 len;
+      SilcUInt32 block_len = silc_cipher_get_block_len(cipher);
+
+      SILC_LOG_DEBUG(("Decrypting the header"));
+
+      /* padding length + src id len + dst id len + header length - 16
+        bytes already decrypted, gives the rest of the encrypted packet */
+      silc_buffer_push(buffer, block_len);
+      len = (((SilcUInt8)buffer->data[4] + (SilcUInt8)buffer->data[6] + 
+             (SilcUInt8)buffer->data[7] + SILC_PACKET_HEADER_LEN) -
+            block_len);
+
+      if (len > buffer->len) {
+       SILC_LOG_ERROR(("Garbage in header of packet, bad packet length, "
+                       "packet dropped"));
+       return -1;
+      }
+      silc_buffer_pull(buffer, block_len);
+      if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data,
+                              len, NULL)) {
+       SILC_LOG_ERROR(("silc_cipher_decrypt failed"));
+       return -1;
       }
     }
-  
-    SILC_LOG_DEBUG(("Decrypting rest of the header"));
-
-    SILC_GET16_MSB(len1, &buffer->data[4]);
-    SILC_GET16_MSB(len2, &buffer->data[6]);
-
-    truelen = SILC_PACKET_HEADER_LEN + len1 + len2;
-    padlen = SILC_PACKET_PADLEN(truelen);
-    len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    cipher->cipher->decrypt(cipher->context, buffer->data + 2,
-                           buffer->data + 2, len1 - 2,
-                           cipher->iv);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-  }
-
-  return TRUE;
-}
-
-/* Decrypts a packet. This assumes that typical SILC packet is the
-   packet to be decrypted and thus checks for normal and special SILC
-   packets and can handle both of them. This also computes and checks
-   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. 
-
-   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,
-                       SilcPacketCheckDecrypt check_packet,
-                       void *context)
-{
-  int check;
-
-  /* Decrypt start of the packet header */
-  if (cipher)
-    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 (check == TRUE) {
-    /* Normal packet, decrypt rest of the packet */
-    if (!silc_packet_decrypt_rest(cipher, hmac, buffer))
-      return -1;
-
-    /* Check MAC */
-    if (!silc_packet_check_mac(hmac, buffer))
-      return -1;
-
-    return 0;
-  } else {
-    /* Packet requires special handling, decrypt rest of the header.
-       This only decrypts. */
-    if (!silc_packet_decrypt_rest_special(cipher, hmac, buffer))
-      return -1;
-
-    /* Check MAC */
-    if (!silc_packet_check_mac(hmac, buffer))
-      return -1;
 
     return 1;
   }
@@ -628,10 +551,12 @@ int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
    function returns the type of the packet. The data section of the 
    buffer is parsed, not head or tail sections. */
 
-SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
+SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
 {
   SilcBuffer buffer = ctx->buffer;
+  SilcUInt8 tmp;
   int len, ret;
+  SilcUInt8 src_id_len, src_id_type, dst_id_len, dst_id_type, padlen;
 
   SILC_LOG_DEBUG(("Parsing incoming packet"));
 
@@ -646,34 +571,46 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
                             SILC_STR_UI_SHORT(&ctx->truelen),
                             SILC_STR_UI_CHAR(&ctx->flags),
                             SILC_STR_UI_CHAR(&ctx->type),
-                            SILC_STR_UI_SHORT(&ctx->src_id_len),
-                            SILC_STR_UI_SHORT(&ctx->dst_id_len),
-                            SILC_STR_UI_CHAR(&ctx->src_id_type),
+                            SILC_STR_UI_CHAR(&padlen),
+                            SILC_STR_UI_CHAR(&tmp),
+                            SILC_STR_UI_CHAR(&src_id_len),
+                            SILC_STR_UI_CHAR(&dst_id_len),
+                            SILC_STR_UI_CHAR(&src_id_type),
                             SILC_STR_END);
-  if (len == -1)
+  if (len == -1 || tmp != 0)
     return SILC_PACKET_NONE;
 
-  if (ctx->src_id_len > SILC_PACKET_MAX_ID_LEN ||
-      ctx->dst_id_len > SILC_PACKET_MAX_ID_LEN) {
-    SILC_LOG_ERROR(("Bad ID lengths in packet"));
+  if (src_id_len > SILC_PACKET_MAX_ID_LEN ||
+      dst_id_len > SILC_PACKET_MAX_ID_LEN) {
+    SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)",
+                   src_id_len, dst_id_len));
     return SILC_PACKET_NONE;
   }
 
-  /* Calculate length of padding in packet */
-  ctx->padlen = SILC_PACKET_PADLEN(ctx->truelen);
-
   silc_buffer_pull(buffer, len);
   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),
+                                                       src_id_len),
+                            SILC_STR_UI_CHAR(&dst_id_type),
                             SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                       ctx->dst_id_len),
-                            SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
+                                                       dst_id_len),
+                            SILC_STR_UI_XNSTRING(NULL, padlen),
                             SILC_STR_END);
   if (ret == -1)
     return SILC_PACKET_NONE;
 
+  if (src_id_type > SILC_ID_CHANNEL || dst_id_type > SILC_ID_CHANNEL) {
+    SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)",
+                  src_id_type, dst_id_type));
+    return SILC_PACKET_NONE;
+  }
+
+  ctx->src_id_len = src_id_len;
+  ctx->dst_id_len = dst_id_len;
+  ctx->src_id_type = src_id_type;
+  ctx->dst_id_type = dst_id_type;
+  ctx->padlen = padlen;
+
   silc_buffer_push(buffer, len);
 
   SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len), 
@@ -694,10 +631,13 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
    the header in a way that it does not take the data area into account
    and parses the header and padding area only. */
 
-SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
+SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
+                                        SilcCipher cipher)
 {
   SilcBuffer buffer = ctx->buffer;
-  int len, tmplen, ret;
+  SilcUInt8 tmp;
+  int len, ret;
+  SilcUInt8 src_id_len, src_id_type, dst_id_len, dst_id_type, padlen;
 
   SILC_LOG_DEBUG(("Parsing incoming packet"));
 
@@ -712,36 +652,49 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
                             SILC_STR_UI_SHORT(&ctx->truelen),
                             SILC_STR_UI_CHAR(&ctx->flags),
                             SILC_STR_UI_CHAR(&ctx->type),
-                            SILC_STR_UI_SHORT(&ctx->src_id_len),
-                            SILC_STR_UI_SHORT(&ctx->dst_id_len),
-                            SILC_STR_UI_CHAR(&ctx->src_id_type),
+                            SILC_STR_UI_CHAR(&padlen),
+                            SILC_STR_UI_CHAR(&tmp),
+                            SILC_STR_UI_CHAR(&src_id_len),
+                            SILC_STR_UI_CHAR(&dst_id_len),
+                            SILC_STR_UI_CHAR(&src_id_type),
                             SILC_STR_END);
-  if (len == -1)
+  if (len == -1 || tmp != 0) {
+    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
     return SILC_PACKET_NONE;
+  }
 
-  if (ctx->src_id_len > SILC_PACKET_MAX_ID_LEN ||
-      ctx->dst_id_len > SILC_PACKET_MAX_ID_LEN) {
-    SILC_LOG_ERROR(("Bad ID lengths in packet"));
+  if (src_id_len > SILC_PACKET_MAX_ID_LEN ||
+      dst_id_len > SILC_PACKET_MAX_ID_LEN) {
+    SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)",
+                   src_id_len, dst_id_len));
     return SILC_PACKET_NONE;
   }
 
-  /* Calculate length of padding in packet. As this is special packet
-     the data area is not used in the padding calculation as it won't
-     be decrypted by the caller. */
-  tmplen = SILC_PACKET_HEADER_LEN + ctx->src_id_len + ctx->dst_id_len;
-  ctx->padlen = SILC_PACKET_PADLEN(tmplen);
-
   silc_buffer_pull(buffer, len);
   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),
+                                                       src_id_len),
+                            SILC_STR_UI_CHAR(&dst_id_type),
                             SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                       ctx->dst_id_len),
-                            SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
+                                                       dst_id_len),
+                            SILC_STR_UI_XNSTRING(NULL, padlen),
                             SILC_STR_END);
-  if (ret == -1)
+  if (ret == -1) {
+    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
     return SILC_PACKET_NONE;
+  }
+
+  if (src_id_type > SILC_ID_CHANNEL || dst_id_type > SILC_ID_CHANNEL) {
+    SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)",
+                  src_id_type, dst_id_type));
+    return SILC_PACKET_NONE;
+  }
+
+  ctx->src_id_len = src_id_len;
+  ctx->dst_id_len = dst_id_len;
+  ctx->src_id_type = src_id_type;
+  ctx->dst_id_type = dst_id_type;
+  ctx->padlen = padlen;
 
   silc_buffer_push(buffer, len);
 
@@ -759,9 +712,11 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
 
 /* Allocate packet context */
 
-SilcPacketContext *silc_packet_context_alloc()
+SilcPacketContext *silc_packet_context_alloc(void)
 {
   SilcPacketContext *ctx = silc_calloc(1, sizeof(*ctx));
+  if (!ctx)
+    return NULL;
   ctx->users++;
   return ctx;
 }