gnutls-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU gnutls branch, master, updated. gnutls_2_11_6-269-gb6b52f6


From: Nikos Mavrogiannopoulos
Subject: [SCM] GNU gnutls branch, master, updated. gnutls_2_11_6-269-gb6b52f6
Date: Wed, 09 Mar 2011 20:13:43 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU gnutls".

http://git.savannah.gnu.org/cgit/gnutls.git/commit/?id=b6b52f6c19ef13337892be120d29f62526fbb15d

The branch, master has been updated
       via  b6b52f6c19ef13337892be120d29f62526fbb15d (commit)
      from  789b253b7946c1c0136c4f795afa37ffc75fdd80 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit b6b52f6c19ef13337892be120d29f62526fbb15d
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Sat Mar 5 19:28:46 2011 +0100

    Added intermediate handshake layer that will order handshake packets and 
drop duplicates.

-----------------------------------------------------------------------

Summary of changes:
 lib/ext_session_ticket.c        |   32 ++--
 lib/gnutls_buffers.c            |  436 +++++++++++++++++++++++++++++++--------
 lib/gnutls_buffers.h            |   55 ++++-
 lib/gnutls_cipher.c             |    3 +-
 lib/gnutls_dtls.c               |    4 +-
 lib/gnutls_errors.c             |    2 +
 lib/gnutls_handshake.c          |  353 ++++++--------------------------
 lib/gnutls_handshake.h          |    6 +-
 lib/gnutls_int.h                |   72 ++++---
 lib/gnutls_kx.c                 |  253 +++++++++++------------
 lib/gnutls_mbuffers.c           |   70 +++++--
 lib/gnutls_mbuffers.h           |   26 ++-
 lib/gnutls_record.c             |   68 ++++--
 lib/gnutls_state.c              |    9 +-
 lib/gnutls_str.c                |    2 +
 lib/gnutls_v2_compat.c          |    3 -
 lib/includes/gnutls/gnutls.h.in |    5 +-
 17 files changed, 772 insertions(+), 627 deletions(-)

diff --git a/lib/ext_session_ticket.c b/lib/ext_session_ticket.c
index f51c127..74fe174 100644
--- a/lib/ext_session_ticket.c
+++ b/lib/ext_session_ticket.c
@@ -690,8 +690,9 @@ _gnutls_send_new_session_ticket (gnutls_session_t session, 
int again)
 int
 _gnutls_recv_new_session_ticket (gnutls_session_t session)
 {
-  uint8_t *data = NULL, *p;
+  uint8_t *p;
   int data_size;
+  gnutls_buffer_st buf;
   uint32_t lifetime_hint;
   uint16_t ticket_len;
   int ret;
@@ -711,34 +712,35 @@ _gnutls_recv_new_session_ticket (gnutls_session_t session)
   if (!priv->session_ticket_renew)
     return 0;
 
-  ret = _gnutls_recv_handshake (session, &data, &data_size,
+  ret = _gnutls_recv_handshake (session, 
                                 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
-                                MANDATORY_PACKET);
+                                MANDATORY_PACKET, &buf);
   if (ret < 0)
     {
       gnutls_assert ();
       return ret;
     }
 
-  p = data;
-  DECR_LENGTH_COM (data_size, 4, goto error);
+  p = buf.data;
+  data_size = buf.length;
+
+  DECR_LENGTH_COM (data_size, 4, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto 
error);
   lifetime_hint = _gnutls_read_uint32 (p);
   p += 4;
 
-  DECR_LENGTH_COM (data_size, 2, goto error);
+  DECR_LENGTH_COM (data_size, 2, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto 
error);
   ticket_len = _gnutls_read_uint16 (p);
   p += 2;
 
-  DECR_LENGTH_COM (data_size, ticket_len, goto error);
+  DECR_LENGTH_COM (data_size, ticket_len, ret = 
GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error);
   priv->session_ticket = gnutls_realloc (priv->session_ticket, ticket_len);
   if (!priv->session_ticket)
     {
       gnutls_assert ();
-      gnutls_free (data);
-      return GNUTLS_E_MEMORY_ERROR;
+      ret = GNUTLS_E_MEMORY_ERROR;
+      goto error;
     }
   memcpy (priv->session_ticket, p, ticket_len);
-  gnutls_free (data);
   priv->session_ticket_len = ticket_len;
 
   /* Discard the current session ID.  (RFC5077 3.4) */
@@ -750,13 +752,15 @@ _gnutls_recv_new_session_ticket (gnutls_session_t session)
       gnutls_assert ();
       gnutls_free (priv->session_ticket);
       priv->session_ticket = NULL;
-      return GNUTLS_E_INTERNAL_ERROR;
+      ret = GNUTLS_E_INTERNAL_ERROR;
+      goto error;
     }
-  return 0;
+  ret = 0;
 
 error:
-  gnutls_free (data);
-  return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+  _gnutls_buffer_clear (&buf);
+  
+  return ret;
 }
 
 #endif
diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c
index 5c88319..c8ad1dc 100644
--- a/lib/gnutls_buffers.c
+++ b/lib/gnutls_buffers.c
@@ -168,9 +168,9 @@ _gnutls_dgram_read (gnutls_session_t session, mbuffer_st 
**bufel,
   gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
 
   if (recv_size > max_size)
-    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+    recv_size = max_size;
 
-  *bufel = _mbuffer_alloc (0, _gnutls_get_max_decrypted_data(session));
+  *bufel = _mbuffer_alloc (0, max_size);
   if (*bufel == NULL)
     return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
 
@@ -179,7 +179,6 @@ _gnutls_dgram_read (gnutls_session_t session, mbuffer_st 
**bufel,
   session->internals.direction = 0;
 
   reset_errno (session);
-
   i = pull_func (fd, ptr, recv_size);
 
   if (i < 0)
@@ -214,7 +213,7 @@ _gnutls_dgram_read (gnutls_session_t session, mbuffer_st 
**bufel,
           /* If we get here, we likely have a stream socket.
            * FIXME: this probably breaks DCCP. */
           gnutls_assert ();
-          ret = GNUTLS_E_INTERNAL_ERROR;
+          ret = 0;
           goto cleanup;
         }
 
@@ -478,14 +477,6 @@ _gnutls_io_read_buffered (gnutls_session_t session, size_t 
total,
         }
     }
 
-  if(_gnutls_is_dtls(session)
-     && session->internals.record_recv_buffer.byte_length != 0)
-    {
-      /* Attempt to read across records while using DTLS. */
-      gnutls_assert();
-      return GNUTLS_E_INVALID_REQUEST;
-    }
-
   /* min is over zero. recvdata is the data we must
    * receive in order to return the requested data.
    */
@@ -736,6 +727,9 @@ _gnutls_io_check_recv (gnutls_session_t session, void* 
data, size_t data_size, u
   else return GNUTLS_E_TIMEDOUT;
 }
 
+/* HANDSHAKE buffers part 
+ */
+
 /* This function writes the data that are left in the
  * Handshake write buffer (ie. because the previous write was
  * interrupted.
@@ -831,109 +825,385 @@ _gnutls_handshake_io_cache_int (gnutls_session_t 
session,
   return 0;
 }
 
-/* Skips a handshake packet
+static int handshake_compare(const void* _e1, const void* _e2)
+{
+const handshake_buffer_st* e1 = _e1;
+const handshake_buffer_st* e2 = _e2;
+
+  if (e1->sequence <= e2->sequence)
+    return 1;
+  else
+    return -1;
+}
+
+#define SSL2_HEADERS 1
+static int
+parse_handshake_header (gnutls_session_t session, mbuffer_st* bufel, 
gnutls_handshake_description_t htype, 
+    handshake_buffer_st* hsk)
+{
+  uint8_t *dataptr = NULL;      /* for realloc */
+  size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session), data_size;
+
+  /* Note: SSL2_HEADERS == 1 */
+  if (_mbuffer_get_udata_size(bufel) < handshake_header_size)
+    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+  dataptr = _mbuffer_get_udata_ptr(bufel);
+
+  /* if reading a client hello of SSLv2 */
+  if (!IS_DTLS(session) && htype == GNUTLS_HANDSHAKE_CLIENT_HELLO &&
+    bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
+    {
+      hsk->length = _mbuffer_get_udata_size(bufel) - SSL2_HEADERS;    /* we've 
read the first byte */
+
+      handshake_header_size = SSL2_HEADERS; /* we've already read one byte */
+
+      if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO)
+        return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+      hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;
+
+      hsk->sequence = 0;
+      hsk->start_offset = 0;
+      hsk->end_offset = hsk->length;
+    }
+  else /* TLS handshake headers */
+    {
+
+      hsk->htype = dataptr[0];
+
+      /* we do not use DECR_LEN because we know
+       * that the packet has enough data.
+       */
+      hsk->length = _gnutls_read_uint24 (&dataptr[1]);
+      handshake_header_size = HANDSHAKE_HEADER_SIZE(session);
+
+      if (IS_DTLS(session))
+        {
+          hsk->sequence = _gnutls_read_uint16 (&dataptr[4]);
+          hsk->start_offset = _gnutls_read_uint24 (&dataptr[6]);
+          hsk->end_offset = hsk->start_offset + _gnutls_read_uint24 
(&dataptr[9]);
+        }
+      else
+        {
+          hsk->sequence = 0;
+          hsk->start_offset = 0;
+          hsk->end_offset = _mbuffer_get_udata_size(bufel) - 
handshake_header_size;
+        }
+    }
+
+  /* make the length offset */
+  if (hsk->end_offset > 0) hsk->end_offset--;
+
+  _gnutls_handshake_log ("HSK[%p]: %s was received. Length %d, frag offset %d, 
frag length: %d, sequence: %d\n",
+                         session, _gnutls_handshake2str (hsk->htype),
+                         (int) hsk->length, hsk->start_offset, 
hsk->end_offset-hsk->start_offset+1, (int)hsk->sequence);
+
+  hsk->header_size = handshake_header_size;
+  memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel), handshake_header_size);
+
+  data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size;
+
+  if (hsk->length > 0 && (hsk->start_offset >= hsk->end_offset ||
+      hsk->end_offset-hsk->start_offset >=  data_size ||
+      hsk->end_offset >= hsk->length))
+    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+  else if (hsk->length == 0 && hsk->end_offset != 0 && hsk->start_offset != 0)
+    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+  
+  return handshake_header_size;
+}
+
+static void _gnutls_handshake_buffer_move(handshake_buffer_st* dst, 
handshake_buffer_st* src)
+{
+  memcpy(dst, src, sizeof(*dst));
+  memset(src, 0, sizeof(*src));
+  src->htype = -1;
+}
+
+/* will merge the given handshake_buffer_st to the handshake_recv_buffer
+ * list. The given hsk packet will be released in any case (success or 
failure).
  */
-int
-_gnutls_handshake_io_recv_skip (gnutls_session_t session,
-                               content_type_t type,
-                               gnutls_handshake_description_t htype,
-                               size_t ptr_size)
+static int merge_handshake_packet(gnutls_session_t session, 
handshake_buffer_st* hsk)
 {
-  opaque * ptr;
-  int ret;
+int exists = 0, i, pos = 0;
+int ret;
+
+  for (i=0;i<session->internals.handshake_recv_buffer_size;i++)
+    {
+      if (session->internals.handshake_recv_buffer[i].htype == hsk->htype)
+        {
+          exists = 1;
+          pos = i;
+          break;
+        }
+    }
 
-  if (ptr_size == 0) return 0;
+  if (exists == 0)
+    pos = session->internals.handshake_recv_buffer_size;
 
-  ptr = gnutls_malloc(ptr_size);
-  if (ptr == NULL)
-    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+  if (pos > MAX_HANDSHAKE_MSGS)
+    return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
 
-  ret = _gnutls_handshake_io_recv_int(session, type, htype, ptr, ptr_size);
-  gnutls_free(ptr);
+  if (exists == 0)
+    {
+      if (hsk->length > 0 && hsk->end_offset > 0 && 
hsk->end_offset-hsk->start_offset+1 != hsk->length)
+        {
+          ret = _gnutls_buffer_resize(&hsk->data, hsk->length);
+          if (ret < 0)
+            return gnutls_assert_val(ret);
+  
+          hsk->data.length = hsk->length;
+          
+          memmove(&hsk->data.data[hsk->start_offset], hsk->data.data, 
hsk->end_offset-hsk->start_offset+1);
+        }
+      
+      session->internals.handshake_recv_buffer_size++;
+      
_gnutls_handshake_buffer_move(&session->internals.handshake_recv_buffer[pos], 
hsk);
 
-  if (ret < 0)
-    return gnutls_assert_val(ret);
+      /* rewrite headers to make them look as each packet came as a single 
fragment */
+      _gnutls_write_uint24(0, &hsk->header[6]);
+      _gnutls_write_uint24(hsk->length, &hsk->header[9]);
+    }
+  else
+    {
+      if (hsk->start_offset < 
session->internals.handshake_recv_buffer[pos].start_offset &&
+        hsk->end_offset >= 
session->internals.handshake_recv_buffer[pos].start_offset)
+        {
+          
memcpy(&session->internals.handshake_recv_buffer[pos].data.data[hsk->start_offset],
 
+            hsk->data.data, hsk->data.length);
+          session->internals.handshake_recv_buffer[pos].start_offset = 
hsk->start_offset;
+          session->internals.handshake_recv_buffer[pos].end_offset = 
+            MIN(hsk->end_offset, 
session->internals.handshake_recv_buffer[pos].end_offset);
+        }
+      else if (hsk->end_offset > 
session->internals.handshake_recv_buffer[pos].end_offset &&
+        hsk->start_offset <= 
session->internals.handshake_recv_buffer[pos].end_offset+1)
+        {
+          
memcpy(&session->internals.handshake_recv_buffer[pos].data.data[hsk->start_offset],
 
+            hsk->data.data, hsk->data.length);
+
+          session->internals.handshake_recv_buffer[pos].end_offset = 
hsk->end_offset;
+          session->internals.handshake_recv_buffer[pos].start_offset = 
+            MIN(hsk->start_offset, 
session->internals.handshake_recv_buffer[pos].start_offset);
+        }
+      _gnutls_handshake_buffer_clear(hsk);
+    }
 
   return 0;
 }
 
+#define LAST_ELEMENT (session->internals.handshake_recv_buffer_size-1)
+
+/* returns the last stored handshake packet.
+ */
+static int get_last_packet(gnutls_session_t session, 
gnutls_handshake_description_t htype,
+  handshake_buffer_st * hsk)
+{
+handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer;
+
+  if (IS_DTLS(session))
+    {
+      if (session->internals.handshake_recv_buffer_size == 0 ||
+        (session->internals.dtls.hsk_read_seq != 
recv_buf[LAST_ELEMENT].sequence))
+        return gnutls_assert_val(GNUTLS_E_AGAIN);
+
+      if (htype != recv_buf[LAST_ELEMENT].htype)
+        return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
+
+      else if ((recv_buf[LAST_ELEMENT].start_offset == 0 &&
+        recv_buf[LAST_ELEMENT].end_offset == recv_buf[LAST_ELEMENT].length -1) 
|| 
+        recv_buf[LAST_ELEMENT].length == 0)
+        {
+          session->internals.dtls.hsk_read_seq++;
+          _gnutls_handshake_buffer_move(hsk, &recv_buf[LAST_ELEMENT]);
+          session->internals.handshake_recv_buffer_size--;
+
+          return 0;
+        }
+      else
+        return gnutls_assert_val(GNUTLS_E_AGAIN);
+    }
+  else /* TLS */
+    {
+      if (session->internals.handshake_recv_buffer_size > 0 && 
recv_buf[0].length == recv_buf[0].data.length)
+        {
+          _gnutls_handshake_buffer_move(hsk, &recv_buf[0]);
+          return 0;
+        }
+      else
+        return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+    }
+}
+
 /* This is a receive function for the gnutls handshake 
  * protocol. Makes sure that we have received all data.
  */
 ssize_t
 _gnutls_handshake_io_recv_int (gnutls_session_t session,
-                               content_type_t type,
                                gnutls_handshake_description_t htype,
-                               void *iptr, size_t ptr_size)
+                               handshake_buffer_st * hsk)
 {
-  size_t left;
-  ssize_t i;
-  opaque *ptr;
-  size_t dsize;
+  gnutls_datum_t msg;
+  mbuffer_st* bufel = NULL, *prev = NULL;
+  int ret;
+  size_t data_size;
+  handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer;
 
-  ptr = iptr;
-  left = ptr_size;
+  ret = get_last_packet(session, htype, hsk);
+  if (ret >= 0)
+    return ret;
 
-  if (ptr_size == 0 || iptr == NULL)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INVALID_REQUEST;
-    }
+  /* if we don't have a complete message waiting for us, try 
+   * receiving more */
+  ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg);
+  if (bufel == NULL)
+    return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
 
-  if (session->internals.handshake_recv_buffer.length > 0)
+  if (!IS_DTLS(session))
     {
-      size_t tmp;
+      ssize_t remain, append, header_size;
 
-      /* if we have already received some data */
-      if (ptr_size <= session->internals.handshake_recv_buffer.length)
+      do
         {
-          /* if requested less data then return it.
+          if (bufel->type != GNUTLS_HANDSHAKE)
+            return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+          /* if we have a half received message the complete it.
            */
-          gnutls_assert ();
+          remain =  recv_buf[0].length -
+                recv_buf[0].data.length;
 
-          tmp = ptr_size;
-          _gnutls_buffer_pop_data (&session->internals.handshake_recv_buffer,
-                                   iptr, &tmp);
-          return tmp;
-        }
-      gnutls_assert ();
+          /* this is the rest of a previous message */
+          if (recv_buf[0].length > 0 && remain > 0)
+            {
+              if (msg.size <= remain)
+                append = msg.size;
+              else
+                append = remain;
+                  
+              ret = _gnutls_buffer_append_data(&recv_buf[0].data, msg.data, 
append);
+              if (ret < 0)
+                return gnutls_assert_val(ret);
+
+              _mbuffer_head_remove_bytes(&session->internals.record_buffer, 
append);
+            }
+          else /* received new message */
+            {
+              ret = parse_handshake_header(session, bufel, htype, 
&recv_buf[0]);
+              if (ret < 0)
+                return gnutls_assert_val(ret);
 
-      tmp = ptr_size;
-      _gnutls_buffer_pop_data (&session->internals.handshake_recv_buffer,
-                               iptr, &tmp);
-      left -= tmp;
-    }
+              header_size = ret;
+              session->internals.handshake_recv_buffer_size = 1;
 
-  while (left > 0)
+              if (htype != recv_buf[0].htype)
+                { /* an unexpected packet */
+                  return 
gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
+                }
+
+              _mbuffer_set_uhead_size(bufel, header_size);
+
+              data_size = MIN(recv_buf[0].length, 
_mbuffer_get_udata_size(bufel));
+              ret = _gnutls_buffer_append_data(&recv_buf[0].data, 
_mbuffer_get_udata_ptr(bufel), data_size);
+              if (ret < 0)
+                return gnutls_assert_val(ret);
+
+              _mbuffer_head_remove_bytes(&session->internals.record_buffer, 
data_size+header_size);
+            }
+
+          /* if packet is complete then return it
+           */
+          if (recv_buf[0].length ==
+                recv_buf[0].data.length)
+            {
+              return get_last_packet(session, htype, hsk);
+            }
+          bufel = _mbuffer_head_get_first(&session->internals.record_buffer, 
&msg);
+        } 
+      while(bufel != NULL);
+    
+      /* if we are here it means that the received packets were not
+       * enough to complete the handshake packet.
+       */
+      return gnutls_assert_val(GNUTLS_E_AGAIN);
+    }
+  else /* DTLS */
     {
-      dsize = ptr_size - left;
-      i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left, NULL);
-      if (i < 0)
-        {
+      handshake_buffer_st tmp;
 
-          if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN))
+      do
+        {
+          /* we now 
+           * 0. parse headers
+           * 1. insert to handshake_recv_buffer
+           * 2. sort handshake_recv_buffer on sequence numbers
+           * 3. return first packet if completed or GNUTLS_E_AGAIN.
+           */
+          do
             {
-              gnutls_assert ();
+              if (bufel->type != GNUTLS_HANDSHAKE)
+                {
+                  gnutls_assert();
+                  goto next; /* ignore packet */
+                }
+
+              _gnutls_handshake_buffer_init(&tmp);
+
+              ret = parse_handshake_header(session, bufel, htype, &tmp);
+              if (ret < 0)
+                {
+                  gnutls_assert();
+                  _gnutls_audit_log("Invalid handshake packet headers. 
Discarding.\n");
+                  break;
+                }
+
+              _mbuffer_consume(&session->internals.record_buffer, bufel, ret);
+
+              data_size = MIN(tmp.length, tmp.end_offset-tmp.start_offset+1);
+
+              ret = _gnutls_buffer_append_data(&tmp.data, 
_mbuffer_get_udata_ptr(bufel), data_size);
+              if (ret < 0)
+                return gnutls_assert_val(ret);
+
+              _mbuffer_consume(&session->internals.record_buffer, bufel, 
data_size);
+
+              ret = merge_handshake_packet(session, &tmp);
+              if (ret < 0)
+                return gnutls_assert_val(ret);
 
-              _gnutls_buffer_append_data (&session->internals.
-                                          handshake_recv_buffer, iptr, dsize);
             }
+          while(_mbuffer_get_udata_size(bufel) > 0);
+
+          prev = bufel;
+          bufel = _mbuffer_dequeue(&session->internals.record_buffer, bufel);
 
-          return i;
+          _mbuffer_xfree(&prev);
+          continue;
+
+next:
+          bufel = _mbuffer_head_get_next(bufel, NULL);
         }
-      else
+      while(bufel != NULL);
+
+      /* sort in descending order */
+      if (session->internals.handshake_recv_buffer_size > 1)
+        qsort(recv_buf, session->internals.handshake_recv_buffer_size,
+          sizeof(recv_buf[0]), handshake_compare);
+
+      while(session->internals.handshake_recv_buffer_size > 0 &&
+        recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq)
         {
-          if (i == 0)
-            break;              /* EOF */
+          _gnutls_audit_log("Discarded replayed handshake packet with sequence 
%d\n", tmp.sequence);
+          _gnutls_handshake_buffer_clear(&recv_buf[LAST_ELEMENT]);
+          session->internals.handshake_recv_buffer_size--;
         }
 
-      left -= i;
-
+        return get_last_packet(session, htype, hsk);
     }
-
-  session->internals.handshake_recv_buffer.length = 0;
-
-  return ptr_size - left;
 }
 
 /* Buffer for handshake packets. Keeps the packets in order
@@ -1005,15 +1275,3 @@ _gnutls_handshake_buffer_empty (gnutls_session_t session)
 
   return 0;
 }
-
-
-int
-_gnutls_handshake_buffer_clear (gnutls_session_t session)
-{
-
-  _gnutls_buffers_log ("BUF[HSK]: Cleared Data from buffer\n");
-  _gnutls_buffer_clear (&session->internals.handshake_hash_buffer);
-
-  return 0;
-}
-
diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h
index 2cb42f3..0293477 100644
--- a/lib/gnutls_buffers.h
+++ b/lib/gnutls_buffers.h
@@ -49,30 +49,61 @@ ssize_t _gnutls_io_write_buffered (gnutls_session_t session,
 int _gnutls_handshake_buffer_get_size (gnutls_session_t session);
 int _gnutls_handshake_buffer_put (gnutls_session_t session, opaque * data,
                                   size_t length);
-int _gnutls_handshake_buffer_clear (gnutls_session_t session);
 int _gnutls_handshake_buffer_empty (gnutls_session_t session);
 int _gnutls_handshake_buffer_get_ptr (gnutls_session_t session,
                                       opaque ** data_ptr, size_t * length);
 
-#define _gnutls_handshake_io_buffer_clear( session) \
-        _mbuffer_head_clear( &session->internals.handshake_send_buffer); \
-        _gnutls_buffer_clear( &session->internals.handshake_recv_buffer);
-
-ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t, content_type_t,
-                                       gnutls_handshake_description_t, void *,
-                                       size_t);
 int _gnutls_handshake_io_cache_int (gnutls_session_t,
                                      gnutls_handshake_description_t,
                                      mbuffer_st * bufel);
-int
-_gnutls_handshake_io_recv_skip (gnutls_session_t session,
-                               content_type_t type,
+
+ssize_t
+_gnutls_handshake_io_recv_int (gnutls_session_t session,
                                gnutls_handshake_description_t htype,
-                               size_t ptr_size);
+                               handshake_buffer_st * hsk);
 
 ssize_t _gnutls_io_write_flush (gnutls_session_t session);
 int
 _gnutls_io_check_recv (gnutls_session_t session, void* data, size_t data_size, 
unsigned int ms);
 ssize_t _gnutls_handshake_io_write_flush (gnutls_session_t session);
 
+inline static void _gnutls_handshake_buffer_clear(handshake_buffer_st* hsk)
+{
+  _gnutls_buffer_clear(&hsk->data);
+  hsk->htype = -1;
+}
+
+inline static void _gnutls_handshake_buffer_init(handshake_buffer_st* hsk)
+{
+  memset(hsk, 0, sizeof(*hsk));
+  _gnutls_buffer_init(&hsk->data);
+  hsk->htype = -1;
+}
+
+inline static void _gnutls_handshake_recv_buffer_clear(gnutls_session_t 
session)
+{
+int i;
+  for (i=0;i<session->internals.handshake_recv_buffer_size;i++)
+    
_gnutls_handshake_buffer_clear(&session->internals.handshake_recv_buffer[i]);
+  session->internals.handshake_recv_buffer_size = 0;
+}
+
+inline static void _gnutls_handshake_recv_buffer_init(gnutls_session_t session)
+{
+int i;
+  for (i=0;i<MAX_HANDSHAKE_MSGS;i++)
+    {
+      
_gnutls_handshake_buffer_init(&session->internals.handshake_recv_buffer[i]);
+    }
+  session->internals.handshake_recv_buffer_size = 0;
+}
+
+ssize_t
+_gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type,
+                  gnutls_handshake_description_t htype);
+
+#define _gnutls_handshake_io_buffer_clear( session) \
+        _mbuffer_head_clear( &session->internals.handshake_send_buffer); \
+        _gnutls_handshake_recv_buffer_clear( session);
+
 #endif
diff --git a/lib/gnutls_cipher.c b/lib/gnutls_cipher.c
index 64dbbd2..d355151 100644
--- a/lib/gnutls_cipher.c
+++ b/lib/gnutls_cipher.c
@@ -612,7 +612,8 @@ _gnutls_ciphertext2compressed (gnutls_session_t session,
   if (compress_size < length)
     return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
 
-  memmove (compress_data, ciphertext.data, length);
+  if (compress_data != ciphertext.data)
+    memcpy (compress_data, ciphertext.data, length);
 
   return length;
 }
diff --git a/lib/gnutls_dtls.c b/lib/gnutls_dtls.c
index 30ecfc5..36262c9 100644
--- a/lib/gnutls_dtls.c
+++ b/lib/gnutls_dtls.c
@@ -46,7 +46,7 @@ transmit_message (gnutls_session_t session,
   opaque *data, *mtu_data;
   int ret = 0;
   unsigned int offset, frag_len, data_size;
-  const uint mtu = session->internals.dtls.mtu;
+  const uint mtu = gnutls_dtls_get_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE;
 
   if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC)
     {
@@ -96,7 +96,7 @@ transmit_message (gnutls_session_t session,
       /* Fragment length */
       _gnutls_write_uint24 (frag_len, &mtu_data[9]);
 
-      memcpy (&mtu_data[12], data+offset, frag_len);
+      memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len);
 
       _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with "
                        "length: %u, offset: %u, fragment length: %u\n",
diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c
index b5811c0..4d499ab 100644
--- a/lib/gnutls_errors.c
+++ b/lib/gnutls_errors.c
@@ -201,6 +201,8 @@ static const gnutls_error_entry error_algorithms[] = {
 
   ERROR_ENTRY (N_("Too many empty record packets have been received."),
                GNUTLS_E_TOO_MANY_EMPTY_PACKETS, 1),
+  ERROR_ENTRY (N_("Too many handshake packets have been received."),
+               GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS, 1),
   ERROR_ENTRY (N_("The initialization of GnuTLS-extra has failed."),
                GNUTLS_E_INIT_LIBEXTRA, 1),
   ERROR_ENTRY (N_
diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c
index e8c427f..d8dcea4 100644
--- a/lib/gnutls_handshake.c
+++ b/lib/gnutls_handshake.c
@@ -103,7 +103,6 @@ _gnutls_handshake_hash_buffers_clear (gnutls_session_t 
session)
     }
   session->security_parameters.handshake_mac_handle_type = 0;
   session->internals.handshake_mac_handle_init = 0;
-  _gnutls_handshake_buffer_clear (session);
 }
 
 /* this will copy the required values for resuming to
@@ -428,10 +427,6 @@ _gnutls_read_client_hello (gnutls_session_t session, 
opaque * data,
   int len = datalen;
   opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr, *session_id;
 
-  if (session->internals.v2_hello != 0)
-    {                           /* version 2.0 */
-      return _gnutls_read_client_hello_v2 (session, data, datalen);
-    }
   DECR_LEN (len, 2);
 
   _gnutls_handshake_log ("HSK[%p]: Client's version: %d.%d\n", session,
@@ -761,13 +756,14 @@ static int
 _gnutls_recv_finished (gnutls_session_t session)
 {
   uint8_t data[MAX_VERIFY_DATA_SIZE], *vrfy;
+  gnutls_buffer_st buf;
   int data_size;
   int ret;
-  int vrfysize;
+  int vrfy_size;
 
   ret =
-    _gnutls_recv_handshake (session, &vrfy, &vrfysize,
-                            GNUTLS_HANDSHAKE_FINISHED, MANDATORY_PACKET);
+    _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_FINISHED, 
+      MANDATORY_PACKET, &buf);
   if (ret < 0)
     {
       ERR ("recv finished int", ret);
@@ -775,6 +771,8 @@ _gnutls_recv_finished (gnutls_session_t session)
       return ret;
     }
 
+  vrfy = buf.data;
+  vrfy_size = buf.length;
 
   if (gnutls_protocol_get_version (session) == GNUTLS_SSL3)
     {
@@ -785,11 +783,11 @@ _gnutls_recv_finished (gnutls_session_t session)
       data_size = 12;
     }
 
-  if (vrfysize != data_size)
+  if (vrfy_size != data_size)
     {
       gnutls_assert ();
-      gnutls_free (vrfy);
-      return GNUTLS_E_ERROR_IN_FINISHED_PACKET;
+      ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
+      goto cleanup;
     }
 
   if (gnutls_protocol_get_version (session) == GNUTLS_SSL3)
@@ -810,22 +808,21 @@ _gnutls_recv_finished (gnutls_session_t session)
   if (ret < 0)
     {
       gnutls_assert ();
-      gnutls_free (vrfy);
-      return ret;
+      goto cleanup;
     }
 
   if (memcmp (vrfy, data, data_size) != 0)
     {
       gnutls_assert ();
       ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
+      goto cleanup;
     }
-  gnutls_free (vrfy);
 
   ret = _gnutls_ext_sr_finished (session, data, data_size, 1);
   if (ret < 0)
     {
       gnutls_assert ();
-      return ret;
+      goto cleanup;
     }
 
   if ((session->internals.resumed == RESUME_TRUE
@@ -840,8 +837,12 @@ _gnutls_recv_finished (gnutls_session_t session)
       session->internals.cb_tls_unique_len = data_size;
     }
 
+
   session->internals.initial_negotiation_completed = 1;
 
+cleanup:
+  _gnutls_buffer_clear(&buf);
+
   return ret;
 }
 
@@ -1279,185 +1280,6 @@ _gnutls_send_handshake (gnutls_session_t session, 
mbuffer_st * bufel,
   return ret;
 }
 
-#define _gnutls_handshake_header_buffer_clear( session) 
session->internals.handshake_header_buffer.header_size = 0; \
-  session->internals.handshake_header_buffer.sequence = -1; \
-  session->internals.handshake_header_buffer.frag_offset = 0; \
-  session->internals.handshake_header_buffer.frag_length = 0
-
-/* This function will read the handshake header and return it to the caller. 
If the
- * received handshake packet is not the one expected then it buffers the 
header, and
- * returns UNEXPECTED_HANDSHAKE_PACKET.
- *
- * FIXME: This function is complex.
- */
-#define SSL2_HEADERS 1
-static int
-_gnutls_recv_handshake_header (gnutls_session_t session,
-                               gnutls_handshake_description_t type,
-                               gnutls_handshake_description_t * recv_type)
-{
-  int ret;
-  uint32_t length32 = 0;
-  uint8_t *dataptr = NULL;      /* for realloc */
-  size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session);
-
-  /* if we have data into the buffer then return them, do not read the next 
packet.
-   * In order to return we need a full TLS handshake header, or in case of a 
version 2
-   * packet, then we return the first byte.
-   */
-  if (session->internals.handshake_header_buffer.header_size ==
-      handshake_header_size || (session->internals.v2_hello != 0
-                                && type == GNUTLS_HANDSHAKE_CLIENT_HELLO
-                                && session->internals.handshake_header_buffer.
-                                packet_length > 0))
-    {
-
-      *recv_type = session->internals.handshake_header_buffer.recv_type;
-
-      if (*recv_type != type)
-        {
-          gnutls_assert ();
-          _gnutls_handshake_log
-            ("HSK[%p]: Handshake type mismatch (under attack?)\n", session);
-          return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
-        }
-
-      return session->internals.handshake_header_buffer.packet_length;
-    }
-
-  /* Note: SSL2_HEADERS == 1 */
-
-  dataptr = session->internals.handshake_header_buffer.header;
-
-  /* If we haven't already read the handshake headers.
-   */
-  if (session->internals.handshake_header_buffer.header_size < SSL2_HEADERS)
-    {
-      ret =
-        _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE,
-                                       type, dataptr, SSL2_HEADERS);
-
-      if (ret < 0)
-        {
-          return ret;
-        }
-
-      /* The case ret==0 is caught here.
-       */
-      if (ret != SSL2_HEADERS)
-        {
-          gnutls_assert ();
-          return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
-        }
-      session->internals.handshake_header_buffer.header_size = SSL2_HEADERS;
-    }
-
-  if (session->internals.v2_hello == 0
-      || type != GNUTLS_HANDSHAKE_CLIENT_HELLO)
-    {
-      ret =
-        _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE,
-                                       type,
-                                       &dataptr
-                                       [session->
-                                        internals.handshake_header_buffer.
-                                        header_size],
-                                       HANDSHAKE_HEADER_SIZE(session) -
-                                       session->
-                                       internals.handshake_header_buffer.
-                                       header_size);
-      if (ret <= 0)
-        {
-          gnutls_assert ();
-          return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
-        }
-      if ((size_t) ret !=
-          HANDSHAKE_HEADER_SIZE(session) -
-          session->internals.handshake_header_buffer.header_size)
-        {
-          gnutls_assert ();
-          return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
-        }
-      *recv_type = dataptr[0];
-
-      /* we do not use DECR_LEN because we know
-       * that the packet has enough data.
-       */
-      length32 = _gnutls_read_uint24 (&dataptr[1]);
-      handshake_header_size = HANDSHAKE_HEADER_SIZE(session);
-
-      if (IS_DTLS(session))
-        {
-          session->internals.handshake_header_buffer.sequence = 
_gnutls_read_uint16 (&dataptr[4]);
-          session->internals.handshake_header_buffer.frag_offset = 
_gnutls_read_uint24 (&dataptr[6]);
-          session->internals.handshake_header_buffer.frag_length = 
_gnutls_read_uint24 (&dataptr[9]);
-        }
-          
-
-      _gnutls_handshake_log ("HSK[%p]: %s was received [%ld bytes]\n",
-                             session, _gnutls_handshake2str (dataptr[0]),
-                             (long int) (length32 + 
HANDSHAKE_HEADER_SIZE(session)));
-
-    }
-  else
-    {                           /* v2 hello */
-      length32 = session->internals.v2_hello - SSL2_HEADERS;    /* we've read 
the first byte */
-
-      handshake_header_size = SSL2_HEADERS;     /* we've already read one byte 
*/
-
-      *recv_type = dataptr[0];
-
-      _gnutls_handshake_log ("HSK[%p]: %s(v2) was received [%ld bytes]\n",
-                             session, _gnutls_handshake2str (*recv_type),
-                             (long int) (length32 + handshake_header_size));
-
-      /* The IS_DTLS() check is redundant since the record layer will
-       * prevent us from reaching here.
-       */
-      if (IS_DTLS(session) || *recv_type != GNUTLS_HANDSHAKE_CLIENT_HELLO)
-        {                       /* it should be one or nothing */
-          gnutls_assert ();
-          return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
-        }
-    }
-
-  /* put the packet into the buffer */
-  session->internals.handshake_header_buffer.header_size =
-    handshake_header_size;
-  session->internals.handshake_header_buffer.packet_length = length32;
-  session->internals.handshake_header_buffer.recv_type = *recv_type;
-
-  if (IS_DTLS(session))
-    {
-      if ((int)session->internals.handshake_header_buffer.sequence <= 
session->internals.dtls.hsk_read_seq)
-        {
-          _gnutls_audit_log("Dropping replayed handshake packet with sequence 
%d\n", session->internals.handshake_header_buffer.sequence);
-          ret = _gnutls_handshake_io_recv_skip(session, GNUTLS_HANDSHAKE, 
*recv_type, length32);
-          if (ret < 0)
-            return gnutls_assert_val(ret);
-
-          _gnutls_handshake_header_buffer_clear (session);
-          return GNUTLS_E_AGAIN;
-        }
-      session->internals.dtls.hsk_read_seq = 
session->internals.handshake_header_buffer.sequence;
-
-      if (type == GNUTLS_HANDSHAKE_SERVER_HELLO
-        && *recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST)
-        return length32;
-    }
-
-  if (*recv_type != type)
-    {
-      _gnutls_handshake_log ("HSK[%p]: %s was received, expected %s\n",
-                             session, _gnutls_handshake2str (*recv_type),
-                              _gnutls_handshake2str (type));
-      gnutls_assert ();
-      return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
-    }
-
-  return length32;
-}
-
 /* This function will hash the handshake headers and the
  * handshake data.
  */
@@ -1516,94 +1338,44 @@ _gnutls_handshake_hash_add_recvd (gnutls_session_t 
session,
  * passed to _gnutls_recv_hello().
  */
 int
-_gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data,
-                        int *datalen, gnutls_handshake_description_t type,
-                        optional_t optional)
+_gnutls_recv_handshake (gnutls_session_t session, 
+                        gnutls_handshake_description_t type,
+                        optional_t optional, gnutls_buffer_st* buf)
 {
   int ret;
-  uint32_t length32 = 0;
-  opaque *dataptr = NULL;
-  gnutls_handshake_description_t recv_type;
+  handshake_buffer_st hsk;
 
-  ret = _gnutls_recv_handshake_header (session, type, &recv_type);
+  ret =
+    _gnutls_handshake_io_recv_int (session, type, &hsk);
   if (ret < 0)
     {
-      if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET
-          && optional == OPTIONAL_PACKET)
-        {
-          if (datalen != NULL)
-            *datalen = 0;
-          if (data != NULL)
-            *data = NULL;
-          return 0;             /* ok just ignore the packet */
-        }
-
-      return ret;
-    }
-
-  session->internals.last_handshake_in = recv_type;
-
-  length32 = ret;
-
-  if (length32 > 0)
-    dataptr = gnutls_malloc (length32);
-  else if (recv_type != GNUTLS_HANDSHAKE_SERVER_HELLO_DONE)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
-    }
-
-  if (dataptr == NULL && length32 > 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_MEMORY_ERROR;
-    }
-
-  if (datalen != NULL)
-    *datalen = length32;
-
-  if (length32 > 0)
-    {
-      ret =
-        _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE,
-                                       type, dataptr, length32);
-      if (ret <= 0)
+      if (optional == OPTIONAL_PACKET && ret == 
GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
         {
-          gnutls_assert ();
-          if (ret == 0) 
-            ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
-          goto cleanup;
+          if (buf) _gnutls_buffer_init(buf);
+          return 0;
         }
+      return gnutls_assert_val(ret);
     }
 
-  if (data != NULL && length32 > 0)
-    *data = dataptr;
-
-  ret = _gnutls_handshake_hash_add_recvd (session, recv_type,
-                                          session->
-                                          internals.handshake_header_buffer.
-                                          header,
-                                          session->
-                                          internals.handshake_header_buffer.
-                                          header_size, dataptr, length32);
-
-  /* If we fail before this then we will reuse the handshake header
-   * have have received above. if we get here the we clear the handshake
-   * header we received.
-   */
-  _gnutls_handshake_header_buffer_clear (session);
-
+  ret = _gnutls_handshake_hash_add_recvd (session, hsk.htype,
+                                          hsk.header, hsk.header_size,
+                                          hsk.data.data, hsk.data.length);
   if (ret < 0)
     {
       gnutls_assert ();
       goto cleanup;
     }
 
-  switch (recv_type)
+  switch (hsk.htype)
     {
+    case GNUTLS_HANDSHAKE_CLIENT_HELLO_V2:
     case GNUTLS_HANDSHAKE_CLIENT_HELLO:
     case GNUTLS_HANDSHAKE_SERVER_HELLO:
-      ret = _gnutls_recv_hello (session, dataptr, length32);
+      if (hsk.htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
+        ret = _gnutls_read_client_hello_v2 (session, hsk.data.data, 
hsk.data.length);
+      else
+        ret = _gnutls_recv_hello (session, hsk.data.data, hsk.data.length);
+
       if (ret < 0)
         {
           gnutls_assert();
@@ -1622,25 +1394,26 @@ _gnutls_recv_handshake (gnutls_session_t session, 
uint8_t ** data,
 
       break;
     case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST:
-      ret = _gnutls_recv_hello_verify_request (session, dataptr, length32);
+      ret = _gnutls_recv_hello_verify_request (session, hsk.data.data, 
hsk.data.length);
       if (ret < 0)
         {
           gnutls_assert();
           goto cleanup;
         }
       else
-       /* Signal our caller we have received a verification cookie
-          and ClientHello needs to be sent again. */
+        /* Signal our caller we have received a verification cookie
+           and ClientHello needs to be sent again. */
         ret = 1;
        
       goto cleanup; /* caller doesn't need dataptr */
 
       break;
     case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE:
-      if (length32 == 0)
+      if (hsk.data.length == 0)
         ret = 0;
       else
         {
+          gnutls_assert();
           ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
           goto cleanup;
         }
@@ -1653,7 +1426,7 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t 
** data,
     case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
     case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
     case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
-      ret = length32;
+      ret = hsk.data.length;
       break;
     default:
       gnutls_assert ();
@@ -1661,12 +1434,14 @@ _gnutls_recv_handshake (gnutls_session_t session, 
uint8_t ** data,
       goto cleanup;
     }
 
-  return ret;
+  if (buf)
+    {
+      *buf = hsk.data;
+      return ret;
+    }
 
 cleanup:
-  gnutls_free (dataptr);
-  if (data != NULL)
-    *data = NULL;
+  _gnutls_handshake_buffer_clear (&hsk);
   return ret;
 }
 
@@ -2732,29 +2507,28 @@ _gnutls_send_supplemental (gnutls_session_t session, 
int again)
 static int
 _gnutls_recv_supplemental (gnutls_session_t session)
 {
-  uint8_t *data = NULL;
-  int datalen = 0;
+  gnutls_buffer_st buf;
   int ret;
 
   _gnutls_debug_log ("EXT[%p]: Expecting supplemental data\n", session);
 
-  ret = _gnutls_recv_handshake (session, &data, &datalen,
-                                GNUTLS_HANDSHAKE_SUPPLEMENTAL,
-                                OPTIONAL_PACKET);
+  ret = _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_SUPPLEMENTAL,
+                                OPTIONAL_PACKET, &buf);
   if (ret < 0)
     {
       gnutls_assert ();
       return ret;
     }
 
-  ret = _gnutls_parse_supplemental (session, data, datalen);
+  ret = _gnutls_parse_supplemental (session, buf.data, buf.length);
   if (ret < 0)
     {
       gnutls_assert ();
-      return ret;
+      goto cleanup;
     }
 
-  gnutls_free (data);
+cleanup:
+  _gnutls_buffer_clear(&buf);
 
   return ret;
 }
@@ -2899,9 +2673,9 @@ _gnutls_handshake_client (gnutls_session_t session)
       if (_gnutls_is_dtls (session))
         {
           ret =
-            _gnutls_recv_handshake (session, NULL, NULL,
+            _gnutls_recv_handshake (session, 
                   GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST,
-                  OPTIONAL_PACKET);
+                  OPTIONAL_PACKET, NULL);
           STATE = STATE11;
           IMED_RET ("recv hello verify", ret, 1);
 
@@ -2914,9 +2688,9 @@ _gnutls_handshake_client (gnutls_session_t session)
     case STATE2:
       /* receive the server hello */
       ret =
-        _gnutls_recv_handshake (session, NULL, NULL,
+        _gnutls_recv_handshake (session,
                                 GNUTLS_HANDSHAKE_SERVER_HELLO,
-                                MANDATORY_PACKET);
+                                MANDATORY_PACKET, NULL);
       STATE = STATE2;
       IMED_RET ("recv hello", ret, 1);
 
@@ -2955,12 +2729,11 @@ _gnutls_handshake_client (gnutls_session_t session)
       /* receive the server hello done */
       if (session->internals.resumed == RESUME_FALSE)   /* if we are not 
resuming */
         ret =
-          _gnutls_recv_handshake (session, NULL, NULL,
+          _gnutls_recv_handshake (session,
                                   GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
-                                  MANDATORY_PACKET);
+                                  MANDATORY_PACKET, NULL);
       STATE = STATE6;
       IMED_RET ("recv server hello done", ret, 1);
-
     case STATE71:
       if (session->security_parameters.do_send_supplemental)
         {
@@ -3171,9 +2944,9 @@ _gnutls_handshake_server (gnutls_session_t session)
     case STATE0:
     case STATE1:
       ret =
-        _gnutls_recv_handshake (session, NULL, NULL,
+        _gnutls_recv_handshake (session,
                                 GNUTLS_HANDSHAKE_CLIENT_HELLO,
-                                MANDATORY_PACKET);
+                                MANDATORY_PACKET, NULL);
       STATE = STATE1;
       IMED_RET ("recv hello", ret, 1);
 
diff --git a/lib/gnutls_handshake.h b/lib/gnutls_handshake.h
index e320044..6792bbe 100644
--- a/lib/gnutls_handshake.h
+++ b/lib/gnutls_handshake.h
@@ -32,9 +32,9 @@ int _gnutls_recv_hello_request (gnutls_session_t session, 
void *data,
                                 uint32_t data_size);
 int _gnutls_send_hello (gnutls_session_t session, int again);
 int _gnutls_recv_hello (gnutls_session_t session, opaque * data, int datalen);
-int _gnutls_recv_handshake (gnutls_session_t session, uint8_t **, int *,
-                            gnutls_handshake_description_t,
-                            optional_t optional);
+int _gnutls_recv_handshake (gnutls_session_t session, 
+                        gnutls_handshake_description_t type,
+                        optional_t optional, gnutls_buffer_st* buf);
 int _gnutls_generate_session_id (opaque * session_id, uint8_t * len);
 int _gnutls_handshake_common (gnutls_session_t session);
 int _gnutls_handshake_client (gnutls_session_t session);
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 21d9484..74f6e5f 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -153,7 +153,6 @@ typedef enum transport_t
 #define MAX_PAD_SIZE 255
 #define EXTRA_COMP_SIZE 2048
 #define MAX_RECORD_OVERHEAD 
(MAX_CIPHER_BLOCK_SIZE/*iv*/+MAX_PAD_SIZE+EXTRA_COMP_SIZE+MAX_HASH_SIZE/*MAC*/)
-#define MAX_RECORD_OVERHEAD_RT(session) 
(2*MAX_CIPHER_BLOCK_SIZE/*iv+pad*/+MAX_HASH_SIZE)
 #define MAX_RECV_SIZE(session) 
(MAX_RECORD_OVERHEAD+MAX_RECORD_RECV_SIZE(session)+RECORD_HEADER_SIZE(session))
 
 #define TLS_HANDSHAKE_HEADER_SIZE 4
@@ -246,32 +245,65 @@ typedef enum content_type_t
 
 /* Message buffers (mbuffers) structures */
 
+/* this is actually the maximum number of distinct handshake
+ * messages that can arrive in a single flight
+ */
+#define MAX_HANDSHAKE_MSGS 6
+typedef struct
+{
+  /* Handshake layer type and sequence of message */
+  gnutls_handshake_description_t htype;
+  uint32_t length;
+
+  /* valid in DTLS */
+  uint16_t sequence;
+
+  /* indicate whether that message is complete.
+   * complete means start_offset == 0 and end_offset == length
+   */
+  uint32_t start_offset;
+  uint32_t end_offset;
+  
+  opaque header[MAX_HANDSHAKE_HEADER_SIZE];
+  int header_size;
+
+  gnutls_buffer_st data;
+} handshake_buffer_st;
+
 typedef struct mbuffer_st
 {
+  /* when used in mbuffer_head_st */
   struct mbuffer_st *next;
+  struct mbuffer_st *prev;
 
-  gnutls_datum_t msg;
   /* msg->size - mark = number of bytes left to process in this
      message. Mark should only be non-zero when this buffer is the
      head of the queue. */
   size_t mark;
-  unsigned int user_mark;       /* only used during fill in */
+
+
+  /* the data */
+  gnutls_datum_t msg;
   size_t maximum_size;
 
+  /* used during fill in, to separate header from data
+   * body. */
+  unsigned int user_mark;
+
   /* Filled in by record layer on recv:
    * type, record_sequence
    */
 
-  /* Filled in by handshake layer on send:
-   * type, epoch, htype, handshake_sequence
-   */
-
   /* record layer content type */
   content_type_t type;
 
   /* record layer sequence */
   uint64 record_sequence;
 
+  /* Filled in by handshake layer on send:
+   * type, epoch, htype, handshake_sequence
+   */
+
   /* Record layer epoch of message */
   uint16_t epoch;
 
@@ -283,7 +315,7 @@ typedef struct mbuffer_st
 typedef struct mbuffer_head_st
 {
   mbuffer_st *head;
-  mbuffer_st **tail;
+  mbuffer_st *tail;
 
   unsigned int length;
   size_t byte_length;
@@ -569,21 +601,6 @@ typedef struct
 } dtls_st;
 
 
-typedef struct
-{
-  opaque header[MAX_HANDSHAKE_HEADER_SIZE];
-  /* this holds the number of bytes in the handshake_header[] */
-  size_t header_size;
-  /* this holds the length of the handshake packet */
-  size_t packet_length;
-  gnutls_handshake_description_t recv_type;
-  
-  /* DTLS fields */
-  uint16_t sequence;
-  size_t frag_offset;
-  size_t frag_length;
-} handshake_header_buffer_st;
-
 typedef union
 {
   void *ptr;
@@ -652,7 +669,8 @@ typedef struct
    * protocol only. freed using _gnutls_handshake_io_buffer_clear();
    */
   mbuffer_head_st handshake_send_buffer;
-  gnutls_buffer_st handshake_recv_buffer;
+  handshake_buffer_st handshake_recv_buffer[MAX_HANDSHAKE_MSGS];
+  int handshake_recv_buffer_size;
 
   /* this buffer holds a record packet -mostly used for
    * non blocking IO.
@@ -674,12 +692,6 @@ typedef struct
 
   int expire_time;              /* after expire_time seconds this session will 
expire */
   struct mod_auth_st_int *auth_struct;  /* used in handshake packets and KX 
algorithms */
-  int v2_hello;                 /* 0 if the client hello is v3+.
-                                 * non-zero if we got a v2 hello.
-                                 */
-  /* keeps the headers of the handshake packet 
-   */
-  handshake_header_buffer_st handshake_header_buffer;
 
   /* this is the highest version available
    * to the peer. (advertized version).
diff --git a/lib/gnutls_kx.c b/lib/gnutls_kx.c
index 7c390f0..b9e5b3e 100644
--- a/lib/gnutls_kx.c
+++ b/lib/gnutls_kx.c
@@ -449,8 +449,7 @@ cleanup:
 int
 _gnutls_recv_server_kx_message (gnutls_session_t session)
 {
-  uint8_t *data = NULL;
-  int datasize;
+  gnutls_buffer_st buf;
   int ret = 0;
   optional_t optflag = MANDATORY_PACKET;
 
@@ -471,10 +470,9 @@ _gnutls_recv_server_kx_message (gnutls_session_t session)
         optflag = OPTIONAL_PACKET;
 
       ret =
-        _gnutls_recv_handshake (session, &data,
-                                &datasize,
+        _gnutls_recv_handshake (session, 
                                 GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE,
-                                optflag);
+                                optflag, &buf);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -483,9 +481,9 @@ _gnutls_recv_server_kx_message (gnutls_session_t session)
 
       ret =
         session->internals.auth_struct->gnutls_process_server_kx (session,
-                                                                  data,
-                                                                  datasize);
-      gnutls_free (data);
+                                                                  buf.data,
+                                                                  buf.length);
+      _gnutls_buffer_clear(&buf);
 
       if (ret < 0)
         {
@@ -500,8 +498,7 @@ _gnutls_recv_server_kx_message (gnutls_session_t session)
 int
 _gnutls_recv_server_certificate_request (gnutls_session_t session)
 {
-  uint8_t *data;
-  int datasize;
+  gnutls_buffer_st buf;
   int ret = 0;
 
   if (session->internals.
@@ -509,21 +506,23 @@ _gnutls_recv_server_certificate_request (gnutls_session_t 
session)
     {
 
       ret =
-        _gnutls_recv_handshake (session, &data,
-                                &datasize,
+        _gnutls_recv_handshake (session, 
                                 GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST,
-                                OPTIONAL_PACKET);
+                                OPTIONAL_PACKET, &buf);
       if (ret < 0)
         return ret;
 
-      if (ret == 0 && datasize == 0)
-        return 0;               /* ignored */
+      if (ret == 0 && buf.length == 0)
+        {
+          _gnutls_buffer_clear(&buf);
+          return 0;               /* ignored */
+        }
 
       ret =
         session->internals.
-        auth_struct->gnutls_process_server_certificate_request (session, data,
-                                                                datasize);
-      gnutls_free (data);
+        auth_struct->gnutls_process_server_certificate_request (session, 
buf.data,
+                                                                buf.length);
+      _gnutls_buffer_clear (&buf);
       if (ret < 0)
         return ret;
 
@@ -534,8 +533,7 @@ _gnutls_recv_server_certificate_request (gnutls_session_t 
session)
 int
 _gnutls_recv_client_kx_message (gnutls_session_t session)
 {
-  uint8_t *data;
-  int datasize;
+  gnutls_buffer_st buf;
   int ret = 0;
 
 
@@ -544,18 +542,17 @@ _gnutls_recv_client_kx_message (gnutls_session_t session)
     {
 
       ret =
-        _gnutls_recv_handshake (session, &data,
-                                &datasize,
+        _gnutls_recv_handshake (session, 
                                 GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE,
-                                MANDATORY_PACKET);
+                                MANDATORY_PACKET, &buf);
       if (ret < 0)
         return ret;
 
       ret =
         session->internals.auth_struct->gnutls_process_client_kx (session,
-                                                                  data,
-                                                                  datasize);
-      gnutls_free (data);
+                                                                  buf.data,
+                                                                  buf.length);
+      _gnutls_buffer_clear (&buf);
       if (ret < 0)
         return ret;
 
@@ -565,107 +562,102 @@ _gnutls_recv_client_kx_message (gnutls_session_t 
session)
 }
 
 
-
-
 int
 _gnutls_recv_client_certificate (gnutls_session_t session)
 {
-  int datasize;
-  opaque *data;
+  gnutls_buffer_st buf;
   int ret = 0;
   int optional;
 
-  if (session->internals.auth_struct->gnutls_process_client_certificate !=
+  if (session->internals.auth_struct->gnutls_process_client_certificate ==
       NULL)
-    {
+    return 0;
 
-      /* if we have not requested a certificate then just return
-       */
-      if (session->internals.send_cert_req == 0)
-        {
-          return 0;
-        }
+  /* if we have not requested a certificate then just return
+   */
+  if (session->internals.send_cert_req == 0)
+    {
+      return 0;
+    }
 
-      if (session->internals.send_cert_req == GNUTLS_CERT_REQUIRE)
-        optional = MANDATORY_PACKET;
-      else
-        optional = OPTIONAL_PACKET;
+  if (session->internals.send_cert_req == GNUTLS_CERT_REQUIRE)
+    optional = MANDATORY_PACKET;
+  else
+    optional = OPTIONAL_PACKET;
 
-      ret =
-        _gnutls_recv_handshake (session, &data,
-                                &datasize,
-                                GNUTLS_HANDSHAKE_CERTIFICATE_PKT, optional);
+  ret =
+    _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, 
+      optional, &buf);
 
-      if (ret < 0)
+  if (ret < 0)
+    {
+      /* Handle the case of old SSL3 clients who send
+       * a warning alert instead of an empty certificate to indicate
+       * no certificate.
+       */
+      if (optional == OPTIONAL_PACKET &&
+          ret == GNUTLS_E_WARNING_ALERT_RECEIVED &&
+          gnutls_protocol_get_version (session) == GNUTLS_SSL3 &&
+          gnutls_alert_get (session) == GNUTLS_A_SSL3_NO_CERTIFICATE)
         {
-          /* Handle the case of old SSL3 clients who send
-           * a warning alert instead of an empty certificate to indicate
-           * no certificate.
-           */
-          if (optional == OPTIONAL_PACKET &&
-              ret == GNUTLS_E_WARNING_ALERT_RECEIVED &&
-              gnutls_protocol_get_version (session) == GNUTLS_SSL3 &&
-              gnutls_alert_get (session) == GNUTLS_A_SSL3_NO_CERTIFICATE)
-            {
-
-              /* SSL3 does not send an empty certificate,
-               * but this alert. So we just ignore it.
-               */
-              gnutls_assert ();
-              return 0;
-            }
-
-          /* certificate was required 
-           */
-          if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED
-               || ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
-              && optional == MANDATORY_PACKET)
-            {
-              gnutls_assert ();
-              return GNUTLS_E_NO_CERTIFICATE_FOUND;
-            }
-
-          return ret;
-        }
 
-      if (ret == 0 && datasize == 0 && optional == OPTIONAL_PACKET)
-        {
-          /* Client has not sent the certificate message.
-           * well I'm not sure we should accept this
-           * behaviour.
+          /* SSL3 does not send an empty certificate,
+           * but this alert. So we just ignore it.
            */
           gnutls_assert ();
           return 0;
         }
-      ret =
-        session->internals.
-        auth_struct->gnutls_process_client_certificate (session, data,
-                                                        datasize);
 
-      gnutls_free (data);
-      if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND)
+      /* certificate was required 
+       */
+      if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED
+           || ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
+          && optional == MANDATORY_PACKET)
         {
           gnutls_assert ();
-          return ret;
+          return GNUTLS_E_NO_CERTIFICATE_FOUND;
         }
 
-      /* ok we should expect a certificate verify message now 
+      return ret;
+    }
+
+  if (ret == 0 && buf.length == 0 && optional == OPTIONAL_PACKET)
+    {
+      /* Client has not sent the certificate message.
+       * well I'm not sure we should accept this
+       * behaviour.
        */
-      if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND && optional == OPTIONAL_PACKET)
-        ret = 0;
-      else
-        session->key->certificate_requested = 1;
+      gnutls_assert ();
+      ret = 0;
+      goto cleanup;
+    }
+  ret =
+    session->internals.
+    auth_struct->gnutls_process_client_certificate (session, buf.data,
+                                                    buf.length);
 
+  if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND)
+    {
+      gnutls_assert ();
+      goto cleanup;
     }
 
+  /* ok we should expect a certificate verify message now 
+   */
+  if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND && optional == OPTIONAL_PACKET)
+    ret = 0;
+  else
+    session->key->certificate_requested = 1;
+
+cleanup:
+  _gnutls_buffer_clear(&buf);
   return ret;
 }
 
 int
 _gnutls_recv_server_certificate (gnutls_session_t session)
 {
-  int datasize;
-  opaque *data;
+  gnutls_buffer_st buf;
   int ret = 0;
 
   if (session->internals.auth_struct->gnutls_process_server_certificate !=
@@ -673,10 +665,9 @@ _gnutls_recv_server_certificate (gnutls_session_t session)
     {
 
       ret =
-        _gnutls_recv_handshake (session, &data,
-                                &datasize,
+        _gnutls_recv_handshake (session, 
                                 GNUTLS_HANDSHAKE_CERTIFICATE_PKT,
-                                MANDATORY_PACKET);
+                                MANDATORY_PACKET, &buf);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -685,9 +676,9 @@ _gnutls_recv_server_certificate (gnutls_session_t session)
 
       ret =
         session->internals.
-        auth_struct->gnutls_process_server_certificate (session, data,
-                                                        datasize);
-      gnutls_free (data);
+        auth_struct->gnutls_process_server_certificate (session, buf.data,
+                                                        buf.length);
+      _gnutls_buffer_clear(&buf);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -705,45 +696,41 @@ _gnutls_recv_server_certificate (gnutls_session_t session)
 int
 _gnutls_recv_client_certificate_verify_message (gnutls_session_t session)
 {
-  uint8_t *data;
-  int datasize;
+  gnutls_buffer_st buf;
   int ret = 0;
 
 
-  if (session->internals.auth_struct->gnutls_process_client_cert_vrfy != NULL)
-    {
-
-      if (session->internals.send_cert_req == 0 ||
-          session->key->certificate_requested == 0)
-        {
-          return 0;
-        }
-
-      ret =
-        _gnutls_recv_handshake (session, &data,
-                                &datasize,
-                                GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY,
-                                OPTIONAL_PACKET);
-      if (ret < 0)
-        return ret;
+  if (session->internals.auth_struct->gnutls_process_client_cert_vrfy == NULL)
+    return 0;
 
-      if (ret == 0 && datasize == 0
-          && session->internals.send_cert_req == GNUTLS_CERT_REQUIRE)
-        {
-          /* certificate was required */
-          gnutls_assert ();
-          return GNUTLS_E_NO_CERTIFICATE_FOUND;
-        }
+  if (session->internals.send_cert_req == 0 ||
+      session->key->certificate_requested == 0)
+    {
+      return 0;
+    }
 
-      ret =
-        session->internals.
-        auth_struct->gnutls_process_client_cert_vrfy (session, data,
-                                                      datasize);
-      gnutls_free (data);
-      if (ret < 0)
-        return ret;
+  ret =
+    _gnutls_recv_handshake (session, 
+                            GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY,
+                            OPTIONAL_PACKET, &buf);
+  if (ret < 0)
+    return ret;
 
+  if (ret == 0 && buf.length == 0
+      && session->internals.send_cert_req == GNUTLS_CERT_REQUIRE)
+    {
+      /* certificate was required */
+      gnutls_assert ();
+      ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
+      goto cleanup;
     }
 
+  ret =
+    session->internals.
+    auth_struct->gnutls_process_client_cert_vrfy (session, buf.data,
+                                                  buf.length);
+
+cleanup:
+  _gnutls_buffer_clear(&buf);
   return ret;
 }
diff --git a/lib/gnutls_mbuffers.c b/lib/gnutls_mbuffers.c
index a150bce..2eff937 100644
--- a/lib/gnutls_mbuffers.c
+++ b/lib/gnutls_mbuffers.c
@@ -57,7 +57,7 @@ void
 _mbuffer_head_init (mbuffer_head_st * buf)
 {
   buf->head = NULL;
-  buf->tail = &buf->head;
+  buf->tail = NULL;
 
   buf->length = 0;
   buf->byte_length = 0;
@@ -94,8 +94,40 @@ _mbuffer_enqueue (mbuffer_head_st * buf, mbuffer_st * bufel)
   buf->length++;
   buf->byte_length += bufel->msg.size - bufel->mark;
 
-  *(buf->tail) = bufel;
-  buf->tail = &bufel->next;
+  bufel->prev = buf->tail;
+  if (buf->tail != NULL)
+    buf->tail->next = bufel;
+  else
+    buf->head = bufel;
+  buf->tail = bufel;
+}
+
+/* Remove a segment from the buffer.
+ *
+ * Cost: O(1)
+ *
+ * Returns the buffer following it.
+ */
+mbuffer_st *
+_mbuffer_dequeue (mbuffer_head_st * buf, mbuffer_st * bufel)
+{
+mbuffer_st* ret = bufel->next;
+
+  if (buf->tail == bufel) /* if last */
+    buf->tail = bufel->prev;
+  
+  if (buf->head == bufel) /* if first */
+    buf->head = bufel->next;
+
+  if (bufel->prev)
+    bufel->prev->next = bufel->next;
+
+  buf->length--;
+  buf->byte_length -= bufel->msg.size - bufel->mark;
+  
+  bufel->next = bufel->prev = NULL;
+  
+  return ret;
 }
 
 /* Get a reference to the first segment of the buffer and
@@ -110,13 +142,18 @@ _mbuffer_head_pop_first (mbuffer_head_st * buf)
 {
   mbuffer_st *bufel = buf->head;
 
+  if (buf->head == NULL)
+    return NULL;
+
   buf->head = bufel->next;
+  if (bufel->next)
+    bufel->next->prev = NULL;
 
   buf->byte_length -= (bufel->msg.size - bufel->mark);
   buf->length -= 1;
 
   if (!buf->head)
-    buf->tail = &buf->head;
+    buf->tail = NULL;
     
   return bufel;
 }
@@ -159,15 +196,18 @@ _mbuffer_head_get_next (mbuffer_st * cur, gnutls_datum_t 
* msg)
 {
   mbuffer_st *bufel = cur->next;
 
-  if (bufel)
-    {
-      msg->data = bufel->msg.data + bufel->mark;
-      msg->size = bufel->msg.size - bufel->mark;
-    }
-  else
+  if (msg)
     {
-      msg->data = NULL;
-      msg->size = 0;
+      if (bufel)
+        {
+          msg->data = bufel->msg.data + bufel->mark;
+          msg->size = bufel->msg.size - bufel->mark;
+        }
+      else
+        {
+          msg->data = NULL;
+          msg->size = 0;
+        }
     }
   return bufel;
 }
@@ -189,13 +229,15 @@ remove_front (mbuffer_head_st * buf)
 
   bufel = buf->head;
   buf->head = bufel->next;
+  if (bufel->next)
+    bufel->next->prev = NULL;
 
   buf->byte_length -= (bufel->msg.size - bufel->mark);
   buf->length -= 1;
   gnutls_free (bufel);
 
   if (!buf->head)
-    buf->tail = &buf->head;
+    buf->tail = NULL;
 }
 
 /* Remove a specified number of bytes from the start of the buffer.
@@ -266,7 +308,7 @@ _mbuffer_alloc (size_t payload_size, size_t maximum_size)
       return NULL;
     }
 
-  //payload points after the mbuffer_st structure
+  /* payload points after the mbuffer_st structure */
   st->msg.data = (opaque *) st + sizeof (mbuffer_st);
   st->msg.size = payload_size;
   st->mark = 0;
diff --git a/lib/gnutls_mbuffers.h b/lib/gnutls_mbuffers.h
index 96df86e..46ffe60 100644
--- a/lib/gnutls_mbuffers.h
+++ b/lib/gnutls_mbuffers.h
@@ -31,6 +31,7 @@
 void _mbuffer_head_init (mbuffer_head_st * buf);
 void _mbuffer_head_clear (mbuffer_head_st * buf);
 void _mbuffer_enqueue (mbuffer_head_st * buf, mbuffer_st * bufel);
+mbuffer_st* _mbuffer_dequeue (mbuffer_head_st * buf, mbuffer_st * bufel);
 int _mbuffer_head_remove_bytes (mbuffer_head_st * buf, size_t bytes);
 mbuffer_st *_mbuffer_alloc (size_t payload_size, size_t maximum_size);
 
@@ -54,32 +55,45 @@ int _mbuffer_linearize (mbuffer_head_st * buf);
 inline static void
 _mbuffer_set_udata (mbuffer_st * bufel, void *data, size_t data_size)
 {
-  memcpy (bufel->msg.data + bufel->user_mark, data, data_size);
-  bufel->msg.size = data_size + bufel->user_mark;
+  memcpy (bufel->msg.data + bufel->mark + bufel->user_mark, data, data_size);
+  bufel->msg.size = data_size + bufel->user_mark + bufel->mark;
 }
 
 inline static void *
 _mbuffer_get_uhead_ptr (mbuffer_st * bufel)
 {
-  return bufel->msg.data;
+  return bufel->msg.data + bufel->mark;
 }
 
 inline static void *
 _mbuffer_get_udata_ptr (mbuffer_st * bufel)
 {
-  return bufel->msg.data + bufel->user_mark;
+  return bufel->msg.data + bufel->user_mark + bufel->mark;
 }
 
 inline static void
 _mbuffer_set_udata_size (mbuffer_st * bufel, size_t size)
 {
-  bufel->msg.size = size + bufel->user_mark;
+  bufel->msg.size = size + bufel->user_mark + bufel->mark;
 }
 
 inline static size_t
 _mbuffer_get_udata_size (mbuffer_st * bufel)
 {
-  return bufel->msg.size - bufel->user_mark;
+  return bufel->msg.size - bufel->user_mark - bufel->mark;
+}
+
+/* discards size bytes from the begging of the buffer */
+inline static void
+_mbuffer_consume (mbuffer_head_st* buf, mbuffer_st * bufel, size_t size)
+{
+  bufel->user_mark = 0;
+  if (bufel->mark+size < bufel->msg.size)
+    bufel->mark += size;
+  else
+    bufel->mark = bufel->msg.size;
+
+  buf->byte_length -= size;
 }
 
 inline static size_t
diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c
index 61bbc8b..15405be 100644
--- a/lib/gnutls_record.c
+++ b/lib/gnutls_record.c
@@ -780,6 +780,7 @@ struct tls_record_st {
   uint16_t length;
   uint16_t packet_size; /* header_size + length */
   content_type_t type;
+  int v2:1; /* whether an SSLv2 client hello */
   /* the data */
 };
 
@@ -818,15 +819,19 @@ record_read_headers (gnutls_session_t session,
       /* in order to assist the handshake protocol.
        * V2 compatibility is a mess.
        */
-      session->internals.v2_hello = record->length;
+      record->v2 = 1;
 
-      _gnutls_record_log ("REC[%p]: V2 packet received. Length: %d\n",
-                          session, record->length);
+      _gnutls_record_log ("REC[%p]: SSL 2.0 %s packet received. Length: %d\n",
+                          session, 
+                          _gnutls_packet2str (record->type),
+                          record->length);
 
     }
   else 
     {
       /* dtls version 1.0 and TLS version 1.x */
+      record->v2 = 0;
+
       record->type = headers[0];
       record->version[0] = headers[1];
       record->version[1] = headers[2];
@@ -838,6 +843,12 @@ record_read_headers (gnutls_session_t session,
         }
       else
         record->length = _gnutls_read_uint16 (&headers[3]);
+
+      _gnutls_record_log ("REC[%p]: SSL %d.%d %s packet received. Length: 
%d\n",
+                          session, (int)record->version[0], 
(int)record->version[1], 
+                          _gnutls_packet2str (record->type),
+                          record->length);
+
     }
 
   record->packet_size += record->length;
@@ -871,6 +882,8 @@ gnutls_datum_t raw; /* raw headers */
     }
 
   _mbuffer_head_get_first (&session->internals.record_recv_buffer, &raw);
+  if (raw.size < RECORD_HEADER_SIZE(session))
+    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
 
   record_read_headers (session, raw.data, type, htype, record);
 
@@ -932,13 +945,13 @@ gnutls_datum_t raw; /* raw headers */
 /* This will receive record layer packets and add them to 
  * application_data_buffer and handshake_data_buffer.
  */
-static ssize_t
+ssize_t
 _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type,
                   gnutls_handshake_description_t htype)
 {
   uint64 *packet_sequence;
   uint8_t *ciphertext;
-  mbuffer_st* bufel = NULL;
+  mbuffer_st* bufel = NULL, *decrypted = NULL;
   int ret;
   int empty_packet = 0;
   record_parameters_st *record_params;
@@ -1007,30 +1020,35 @@ begin:
    * move on !
    */
   ret = _mbuffer_linearize (&session->internals.record_recv_buffer);
-  if (ret != 0)
-    {
-      gnutls_assert ();
-      return ret;
-    }
-  bufel = _mbuffer_head_pop_first (&session->internals.record_recv_buffer);
-  ciphertext = &bufel->msg.data[record.header_size];
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  bufel = _mbuffer_head_get_first (&session->internals.record_recv_buffer, 
NULL);
+  if (bufel == NULL)
+    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+  decrypted = _mbuffer_alloc(record.length+EXTRA_COMP_SIZE, 
record.length+EXTRA_COMP_SIZE);
+  if (decrypted == NULL)
+    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+  ciphertext = (opaque*)_mbuffer_get_udata_ptr(bufel) + record.header_size;
 
   /* decrypt the data we got. 
    */
   ret =
-    _gnutls_decrypt (session, ciphertext, record.length, bufel->msg.data, 
bufel->maximum_size,
+    _gnutls_decrypt (session, ciphertext, record.length, 
+        _mbuffer_get_udata_ptr(decrypted), _mbuffer_get_udata_size(decrypted),
                     record.type, record_params, packet_sequence);
-  bufel->msg.size = ret;
+  if (ret >= 0) _mbuffer_set_udata_size(decrypted, ret);
 
+  _mbuffer_head_remove_bytes (&session->internals.record_recv_buffer,
+                         record.header_size + record.length);
   if (ret < 0)
     {
       gnutls_assert();
       goto sanity_check_error;
     }
 
-  /* bufel now holds the decrypted data
-   */
-
   /* check for duplicates. We check after the message
    * is processed and authenticated to avoid someone
    * messing with our windows.
@@ -1049,7 +1067,7 @@ begin:
   _gnutls_record_log
     ("REC[%p]: Decrypted Packet[%d] %s(%d) with length: %d\n", session,
      (int) _gnutls_uint64touint32 (packet_sequence),
-     _gnutls_packet2str (record.type), record.type, bufel->msg.size);
+     _gnutls_packet2str (record.type), record.type, 
(int)_mbuffer_get_udata_size(decrypted));
 
   /* increase sequence number 
    */
@@ -1068,16 +1086,21 @@ begin:
  * In that case we go to the beginning and start reading
  * the next packet.
  */
-  if (bufel->msg.size == 0)
+  if (_mbuffer_get_udata_size(decrypted) == 0)
     {
-      _mbuffer_xfree(&bufel);
+      _mbuffer_xfree(&decrypted);
       empty_packet++;
       goto begin;
     }
 
+  if (record.v2)
+    decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;
+  else
+    decrypted->htype = -1;
+
   ret =
     record_add_to_buffers (session, record.type, type, htype, 
-      packet_sequence, bufel);
+      packet_sequence, decrypted);
 
   /* bufel is now either deinitialized or buffered somewhere else */
 
@@ -1094,6 +1117,7 @@ begin:
   return ret;
 
 discard:
+  /* discard the whole received fragment. */
   bufel = _mbuffer_head_pop_first(&session->internals.record_recv_buffer);
   _mbuffer_xfree(&bufel);
   return GNUTLS_E_AGAIN;
@@ -1111,7 +1135,7 @@ sanity_check_error:
   session_invalidate (session);
 
 cleanup:
-  _mbuffer_xfree(&bufel);
+  _mbuffer_xfree(&decrypted);
   return ret;
 
 recv_error:
diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c
index e22dada..e29749e 100644
--- a/lib/gnutls_state.c
+++ b/lib/gnutls_state.c
@@ -246,9 +246,6 @@ _gnutls_handshake_internal_state_init (gnutls_session_t 
session)
   /* by default no selected certificate */
   session->internals.adv_version_major = 0;
   session->internals.adv_version_minor = 0;
-  session->internals.v2_hello = 0;
-  memset (&session->internals.handshake_header_buffer, 0,
-          sizeof (handshake_header_buffer_st));
   session->internals.direction = 0;
 
   /* use out of band data for the last
@@ -259,7 +256,7 @@ _gnutls_handshake_internal_state_init (gnutls_session_t 
session)
 
   session->internals.resumable = RESUME_TRUE;
   
-  session->internals.dtls.hsk_read_seq = -1;
+  session->internals.dtls.hsk_read_seq = 0;
   session->internals.dtls.hsk_write_seq = 0;
 }
 
@@ -323,7 +320,7 @@ gnutls_init (gnutls_session_t * session, 
gnutls_connection_end_t con_end)
   _mbuffer_head_init (&(*session)->internals.record_recv_buffer);
 
   _mbuffer_head_init (&(*session)->internals.handshake_send_buffer);
-  _gnutls_buffer_init (&(*session)->internals.handshake_recv_buffer);
+  _gnutls_handshake_recv_buffer_init(*session);
 
   (*session)->key = gnutls_calloc (1, sizeof (struct gnutls_key_st));
   if ((*session)->key == NULL)
@@ -1352,7 +1349,7 @@ record_parameters_st *params;
 int total = 0, ret, iv_size;
 
   if (session->internals.initial_negotiation_completed == 0)
-    return 0;
+    return RECORD_HEADER_SIZE(session);
 
   ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &params);
   if (ret < 0)
diff --git a/lib/gnutls_str.c b/lib/gnutls_str.c
index edcbb86..c2d53e5 100644
--- a/lib/gnutls_str.c
+++ b/lib/gnutls_str.c
@@ -127,6 +127,8 @@ _gnutls_buffer_append_data (gnutls_buffer_st * dest, const 
void *data,
 {
   size_t tot_len = data_size + dest->length;
 
+  if (data_size == 0) return 0;
+
   if (dest->max_length >= tot_len)
     {
       size_t unused = MEMSUB (dest->data, dest->allocd);
diff --git a/lib/gnutls_v2_compat.c b/lib/gnutls_v2_compat.c
index 020ced2..abc17b7 100644
--- a/lib/gnutls_v2_compat.c
+++ b/lib/gnutls_v2_compat.c
@@ -105,9 +105,6 @@ _gnutls_read_client_hello_v2 (gnutls_session_t session, 
opaque * data,
   uint16_t challenge;
   opaque session_id[TLS_MAX_SESSION_ID_SIZE];
 
-  /* we only want to get here once - only in client hello */
-  session->internals.v2_hello = 0;
-
   DECR_LEN (len, 2);
 
   _gnutls_handshake_log
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 6363df9..c686db6 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -414,7 +414,8 @@ extern "C"
     GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE = 16,
     GNUTLS_HANDSHAKE_FINISHED = 20,
     GNUTLS_HANDSHAKE_SUPPLEMENTAL = 23,
-    GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC = 254
+    GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC = 254,
+    GNUTLS_HANDSHAKE_CLIENT_HELLO_V2 = 1024,
   } gnutls_handshake_description_t;
 
   /**
@@ -1635,7 +1636,7 @@ extern "C"
 #define GNUTLS_E_FILE_ERROR -64
 #define GNUTLS_E_TOO_MANY_EMPTY_PACKETS -78
 #define GNUTLS_E_UNKNOWN_PK_ALGORITHM -80
-
+#define GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS -81
 
   /* returned if libextra functionality was requested but
    * gnutls_global_init_extra() was not called.


hooks/post-receive
-- 
GNU gnutls



reply via email to

[Prev in Thread] Current Thread [Next in Thread]