]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Support scattered write using writev(). This takes
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 10 Jul 2010 21:10:45 +0000 (23:10 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 10 Jul 2010 21:10:45 +0000 (23:10 +0200)
advantage of the new buffering layer and allows queuing of packets
and flushing them. This is currently used for handshake messages
only. Performance-wise the difference of packing several TLS records
in a single write doesn't seem to offer anything over ethernet (that
my tests were on). Probably on links with higher latency there would
be a benefit.

17 files changed:
NEWS
configure.ac
lib/gnutls_alert.c
lib/gnutls_buffers.c
lib/gnutls_buffers.h
lib/gnutls_global.c
lib/gnutls_handshake.c
lib/gnutls_handshake.h
lib/gnutls_int.h
lib/gnutls_mbuffers.c
lib/gnutls_mbuffers.h
lib/gnutls_record.c
lib/gnutls_record.h
lib/gnutls_state.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
libextra/gnutls_ia.c

diff --git a/NEWS b/NEWS
index 32702d84e2eb61e975dad48be82ca87cea2ff85c..2fe8a5f77083a08116fb314ce7367f38f363800b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,10 +5,17 @@ See the end for copying conditions.
 
 * Version 2.11.0 (unreleased)
 
+** libgnutls: support scattered write using writev(). This takes
+advantage of the new buffering layer and allows queuing of packets
+and flushing them. This is currently used for handshake messages
+only.
+
 ** libgnutls: Added gnutls_global_set_mutex() to allow setting
 alternative locking procedures. By default the system available
 locking is used. In *NIX pthreads are used and in windows the
-critical section API.
+critical section API. This follows a different approach than the
+previous versions that depended on libgcrypt initialization. The
+locks are now set by default in systems that support it.
 
 ** libgnutls: Added support for reading DN from EV-certificates.
 New DN values:
@@ -26,8 +33,9 @@ has to be called to register any required md5 handlers.
 code contributed by Jonathan Bastien-Filiatrault.
 
 ** libgnutls: Internal API for extensions augmented to allow
-safe storing and loading of data on resumption. OPRFI extension
-was removed.
+safe storing and loading of data on resumption. This allows writing
+self-contained extensions (when possible). As a side effect
+the OPRFI extension was removed.
 
 ** libgnutls: Added support for DSA-SHA256 and DSA-SHA224
 
index 9e25ea578ec3fe952c21c38d8001154b1e655cc4..0ec3cc4f6d6b0945340862c0ba48a3e110c266f0 100644 (file)
@@ -223,6 +223,7 @@ if test "$gl_gcc_warnings" = yes; then
   nw="$nw -Wconversion"             # Too many warnings for now
   nw="$nw -Wsign-conversion"        # Too many warnings for now
   nw="$nw -Wformat-y2k"             # Too many warnings for now
+  nw="$nw -Wvla"                    # There is no point to avoid C99 variable length arrays
   nw="$nw -Wformat-nonliteral"      # Incompatible with gettext _()
   nw="$nw -Wunsafe-loop-optimizations"
   nw="$nw -Wstrict-overflow"
index 48ea34ac1c8e47094073690496a9d408d8992126..170ca7e257db55c875e19baf4431310646b46383 100644 (file)
@@ -133,7 +133,7 @@ gnutls_alert_send (gnutls_session_t session, gnutls_alert_level_t level,
   _gnutls_record_log ("REC: Sending Alert[%d|%d] - %s\n", data[0],
                      data[1], name);
 
-  if ((ret = _gnutls_send_int (session, GNUTLS_ALERT, -1, data, 2)) >= 0)
+  if ((ret = _gnutls_send_int (session, GNUTLS_ALERT, -1, data, 2, MBUFFER_FLUSH)) >= 0)
     return 0;
   else
     return ret;
index 78aa8b76ee7c926274a4995156f2d61576a5044a..2bad10aa951264f0cbbe398316ecb440f5379a7b 100644 (file)
 
 #include <errno.h>
 
+#ifndef _WIN32
+# include <sys/uio.h>
+#endif
+
 /* We need to disable gnulib's replacement wrappers to get native
    Windows interfaces. */
 #undef recv
@@ -66,6 +70,9 @@
 # define EAGAIN EWOULDBLOCK
 #endif
 
+/* this is the maximum number of messages allowed to queue.
+ */
+#define MAX_QUEUE 16
 
 /**
  * gnutls_transport_set_errno:
@@ -248,6 +255,19 @@ _gnutls_record_buffer_get (content_type_t type,
   return length;
 }
 
+inline static void reset_errno(gnutls_session_t session)
+{  
+  session->internals.errnum = 0;
+}
+
+inline static int get_errno(gnutls_session_t session)
+{
+  if (session->internals.errnum != 0)
+    return session->internals.errnum;
+  else 
+    return session->internals.errno_func(session->internals.transport_recv_ptr);
+}
+
 
 /* This function is like read. But it does not return -1 on error.
  * It does return gnutls_errno instead.
@@ -269,43 +289,13 @@ _gnutls_read (gnutls_session_t session, void *iptr,
   while (left > 0)
     {
 
-      session->internals.errnum = 0;
-
-      if (session->internals._gnutls_pull_func == NULL)
-       {
-         i = recv (GNUTLS_POINTER_TO_INT (fd), &ptr[sizeOfPtr - left],
-                   left, flags);
-#if HAVE_WINSOCK2_H
-         if (i < 0)
-           {
-             int tmperr = WSAGetLastError ();
-             switch (tmperr)
-               {
-               case WSAEWOULDBLOCK:
-                 session->internals.errnum = EAGAIN;
-                 break;
-
-               case WSAEINTR:
-                 session->internals.errnum = EINTR;
-                 break;
+      reset_errno(session);
 
-               default:
-                 session->internals.errnum = EIO;
-                 break;
-               }
-             WSASetLastError (tmperr);
-           }
-#endif
-       }
-      else
-       i = session->internals._gnutls_pull_func (fd,
-                                                 &ptr[sizeOfPtr -
-                                                      left], left);
+      i = session->internals.pull_func (fd, &ptr[sizeOfPtr - left], left);
 
       if (i < 0)
        {
-         int err = session->internals.errnum ? session->internals.errnum
-           : errno;
+         int err = get_errno(session);
 
          _gnutls_read_log ("READ: %d returned from %p, errno=%d gerrno=%d\n",
                            (int) i, fd, errno, session->internals.errnum);
@@ -356,48 +346,53 @@ finish:
   return (sizeOfPtr - left);
 }
 
+
+
 static ssize_t
-_gnutls_write (gnutls_session_t session, void *iptr,
-              size_t sizeOfPtr)
+_gnutls_writev_emu (gnutls_session_t session, const giovec_t * giovec, int giovec_cnt)
 {
-  int i;
+  int ret, j = 0;
   gnutls_transport_ptr_t fd = session->internals.transport_send_ptr;
+  void* iptr;
+  size_t sizeOfPtr;
+  size_t total = 0;
 
-  session->internals.errnum = 0;
-
-  if (session->internals._gnutls_push_func == NULL)
+  for (j=0;j<giovec_cnt;j++)
     {
-      i = send (GNUTLS_POINTER_TO_INT (fd), iptr, sizeOfPtr, 0);
-#if HAVE_WINSOCK2_H
-      if (i < 0)
-       {
-         int tmperr = WSAGetLastError ();
-         switch (tmperr)
-           {
-           case WSAEWOULDBLOCK:
-             session->internals.errnum = EAGAIN;
-             break;
+      sizeOfPtr = giovec[j].iov_len;
+      iptr = giovec[j].iov_base;
+          
+      ret = session->internals.push_func (fd, iptr, sizeOfPtr);
 
-           case WSAEINTR:
-             session->internals.errnum = EINTR;
-             break;
+      if (ret == -1)
+        break;
 
-           default:
-             session->internals.errnum = EIO;
-             break;
-           }
-         WSASetLastError (tmperr);
-       }
-#endif
+      total += ret;
     }
+
+  if (total > 0)
+    return total;
+  
+  return ret;
+}
+
+static ssize_t
+_gnutls_writev (gnutls_session_t session, const giovec_t * giovec, int giovec_cnt)
+{
+  int i;
+  gnutls_transport_ptr_t fd = session->internals.transport_send_ptr;
+
+  reset_errno(session);
+
+  if (session->internals.push_func != NULL)
+    i = _gnutls_writev_emu(session, giovec, giovec_cnt);
   else
-    i = session->internals._gnutls_push_func (fd, iptr, sizeOfPtr);
+    i = session->internals.vec_push_func (fd, giovec, giovec_cnt);
 
   if (i == -1)
     {
-      int err = session->internals.errnum ? session->internals.errnum
-       : errno;
-
+      int err = get_errno(session);
+_gnutls_debug_log("errno: %d\n", err);
       if (err == EAGAIN)
        return GNUTLS_E_AGAIN;
       else if (err == EINTR)
@@ -410,6 +405,7 @@ _gnutls_write (gnutls_session_t session, void *iptr,
     }
   return i;
 }
+
 #define RCVLOWAT session->internals.lowat
 
 /* This function is only used with berkeley style sockets.
@@ -493,7 +489,7 @@ _gnutls_io_read_buffered (gnutls_session_t session, opaque ** iptr,
   /* If an external pull function is used, then do not leave
    * any data into the kernel buffer.
    */
-  if (session->internals._gnutls_pull_func != NULL)
+  if (session->internals.pull_func != NULL)
     {
       recvlowat = 0;
     }
@@ -664,7 +660,7 @@ _gnutls_io_read_buffered (gnutls_session_t session, opaque ** iptr,
  */
 ssize_t
 _gnutls_io_write_buffered (gnutls_session_t session,
-                          mbuffer_st *bufel)
+                          mbuffer_st *bufel, unsigned int mflag)
 {
   mbuffer_head_st * const send_buffer = &session->internals.record_send_buffer;
 
@@ -675,9 +671,14 @@ _gnutls_io_write_buffered (gnutls_session_t session,
      (int)bufel->msg.size, session->internals.transport_recv_ptr,
      (int)send_buffer->byte_length);
 
-  return _gnutls_io_write_flush (session);
+  if (mflag == MBUFFER_FLUSH)
+    return _gnutls_io_write_flush (session);
+  else
+    return bufel->msg.size;
 }
 
+typedef ssize_t (*send_func)(gnutls_session_t, const giovec_t *, int);
+
 /* This function writes the data that are left in the
  * TLS write buffer (ie. because the previous write was
  * interrupted.
@@ -685,53 +686,70 @@ _gnutls_io_write_buffered (gnutls_session_t session,
 ssize_t
 _gnutls_io_write_flush (gnutls_session_t session)
 {
-  mbuffer_head_st * const send_buffer = &session->internals.record_send_buffer;
   gnutls_datum_t msg;
+  mbuffer_head_st * send_buffer = &session->internals.record_send_buffer;
   int ret;
-  ssize_t total = 0;
+  ssize_t sent = 0, tosend = 0;
+  giovec_t iovec[MAX_QUEUE];
+  int i = 0;
+  mbuffer_st* cur;
 
   _gnutls_write_log ("WRITE FLUSH: %d bytes in buffer.\n",
                     (int)send_buffer->byte_length);
 
-  for (_mbuffer_get_head (send_buffer, &msg);
-       msg.data != NULL && msg.size > 0;
-       _mbuffer_get_head (send_buffer, &msg))
+  for (cur=_mbuffer_get_first(send_buffer, &msg);
+          cur != NULL;
+          cur = _mbuffer_get_next (cur, &msg))
+        {
+          iovec[i].iov_base = msg.data;
+          iovec[i++].iov_len = msg.size;
+          tosend += msg.size;
+          
+          /* we buffer up to MAX_QUEUE messages */
+          if (i>=sizeof(iovec)/sizeof(iovec[0]))
+            {
+              gnutls_assert();
+              return GNUTLS_E_INTERNAL_ERROR;
+            }
+        }
+        
+  ret = _gnutls_writev (session, iovec, i);
+  if (ret >= 0)
     {
-      ret = _gnutls_write (session, msg.data, msg.size);
-
-      if (ret >= 0)
-       {
-         _mbuffer_remove_bytes (send_buffer, ret);
-
-         _gnutls_write_log ("WRITE: wrote %d bytes, %d bytes left.\n",
+          _mbuffer_remove_bytes (send_buffer, ret);
+          _gnutls_write_log ("WRITE: wrote %d bytes, %d bytes left.\n",
                             ret, (int)send_buffer->byte_length);
 
-         total += ret;
-       }
-      else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
-       {
-         _gnutls_write_log ("WRITE interrupted: %d bytes left.\n",
+          sent += ret;
+    }
+  else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
+    {
+          _gnutls_write_log ("WRITE interrupted: %d bytes left.\n",
                             (int)send_buffer->byte_length);
          return ret;
-       }
-      else
-       {
-         _gnutls_write_log ("WRITE error: code %d, %d bytes left.\n",
-                            ret, (int)send_buffer->byte_length);
+    }
+  else
+    {
+          _gnutls_write_log ("WRITE error: code %d, %d bytes left.\n",
+                    ret, (int)send_buffer->byte_length);
 
-         gnutls_assert ();
-         return ret;
-       }
+          gnutls_assert ();
+          return ret;
+    }
+
+  if (sent < tosend)
+    {
+      gnutls_assert();
+      return GNUTLS_E_AGAIN;
     }
 
-  return total;
+  return sent;
 }
 
 /* This function writes the data that are left in the
  * Handshake write buffer (ie. because the previous write was
  * interrupted.
  *
- * FIXME: This is practically the same as _gnutls_io_write_flush.
  */
 ssize_t
 _gnutls_handshake_io_write_flush (gnutls_session_t session)
@@ -740,17 +758,18 @@ _gnutls_handshake_io_write_flush (gnutls_session_t session)
   gnutls_datum_t msg;
   int ret;
   ssize_t total = 0;
+  mbuffer_st * cur;
 
   _gnutls_write_log ("HWRITE FLUSH: %d bytes in buffer.\n",
                     (int)send_buffer->byte_length);
 
-  for (_mbuffer_get_head (send_buffer, &msg);
-       msg.data != NULL && msg.size > 0;
-       _mbuffer_get_head (send_buffer, &msg))
+  for (cur = _mbuffer_get_first (send_buffer, &msg);
+       cur != NULL;
+       cur = _mbuffer_get_first (send_buffer, &msg))
     {
       ret = _gnutls_send_int (session, GNUTLS_HANDSHAKE,
                              session->internals.handshake_send_buffer_htype,
-                             msg.data, msg.size);
+                             msg.data, msg.size, 0/* do not flush */);
 
       if (ret >= 0)
        {
@@ -761,12 +780,6 @@ _gnutls_handshake_io_write_flush (gnutls_session_t session)
 
          total += ret;
        }
-      else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
-       {
-         _gnutls_write_log ("HWRITE interrupted: %d bytes left.\n",
-                            (int)send_buffer->byte_length);
-         return ret;
-       }
       else
        {
          _gnutls_write_log ("HWRITE error: code %d, %d bytes left.\n",
@@ -777,17 +790,17 @@ _gnutls_handshake_io_write_flush (gnutls_session_t session)
        }
     }
 
-  return total;
+  return _gnutls_io_write_flush (session);
+
 }
 
 
 /* This is a send function for the gnutls handshake 
  * protocol. Just makes sure that all data have been sent.
  *
- * FIXME: This is practically the same as _gnutls_io_write_buffered.
  */
-ssize_t
-_gnutls_handshake_io_send_int (gnutls_session_t session,
+void
+_gnutls_handshake_io_cache_int (gnutls_session_t session,
                               gnutls_handshake_description_t htype,
                               mbuffer_st *bufel)
 {
@@ -801,7 +814,7 @@ _gnutls_handshake_io_send_int (gnutls_session_t session,
      (int)bufel->msg.size,
      (int)send_buffer->byte_length);
 
-  return _gnutls_handshake_io_write_flush(session);
+  return;
 }
 
 /* This is a receive function for the gnutls handshake 
@@ -969,3 +982,85 @@ _gnutls_handshake_buffer_clear (gnutls_session_t session)
 
   return 0;
 }
+
+/**
+ * gnutls_transport_set_pull_function:
+ * @pull_func: a callback function similar to read()
+ * @session: gnutls session
+ *
+ * This is the function where you set a function for gnutls to receive
+ * data.  Normally, if you use berkeley style sockets, do not need to
+ * use this function since the default (recv(2)) will probably be ok.
+ *
+ * PULL_FUNC is of the form,
+ * ssize_t (*gnutls_pull_func)(gnutls_transport_ptr_t, void*, size_t);
+ **/
+void
+gnutls_transport_set_pull_function (gnutls_session_t session,
+                                   gnutls_pull_func pull_func)
+{
+  session->internals.pull_func = pull_func;
+}
+
+/**
+ * gnutls_transport_set_push_function:
+ * @push_func: a callback function similar to write()
+ * @session: gnutls session
+ *
+ * This is the function where you set a push function for gnutls to
+ * use in order to send data.  If you are going to use berkeley style
+ * sockets, you do not need to use this function since the default
+ * (send(2)) will probably be ok.  Otherwise you should specify this
+ * function for gnutls to be able to send data.
+ *
+ * PUSH_FUNC is of the form,
+ * ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t, const void*, size_t);
+ **/
+void
+gnutls_transport_set_push_function (gnutls_session_t session,
+                                   gnutls_push_func push_func)
+{
+  session->internals.push_func = push_func;
+  session->internals.vec_push_func = NULL;
+}
+
+/**
+ * @session: gnutls session
+ * gnutls_transport_set_push_function2:
+ * @vec_func: a callback function similar to writev()
+ *
+ * This is the function where you set a push function for gnutls to
+ * use in order to send data.  If you are going to use berkeley style
+ * sockets, you do not need to use this function since the default
+ * (send(2)) will probably be ok.  Otherwise you should specify this
+ * function for gnutls to be able to send data.
+ *
+ * PUSH_FUNC is of the form,
+ * ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t, const void*, size_t);
+ **/
+void
+gnutls_transport_set_push_function2 (gnutls_session_t session,
+                                   gnutls_vec_push_func vec_func)
+{
+  session->internals.push_func = NULL;
+  session->internals.vec_push_func = vec_func;
+}
+
+/**
+ * @session: gnutls session
+ * gnutls_transport_set_errno_function:
+ * @errno_func: a callback function similar to write()
+ *
+ * This is the function where you set a function to retrieve errno
+ * after a failed push or pull operation.
+ *
+ * errno_func is of the form,
+ * int (*gnutls_errno_func)(gnutls_transport_ptr_t);
+ * and should return the errno.
+ **/
+void
+gnutls_transport_set_errno_function (gnutls_session_t session,
+                                   gnutls_errno_func errno_func)
+{
+  session->internals.errno_func = errno_func;
+}
index 729bcfc6c17b25e4a78d68e3da6e045719b52fc1..5352874b3d1a341e6f92d8155b202dc1b1a64cb9 100644 (file)
  * USA
  *
  */
+#ifndef GNUTLS_BUFFERS_H
+# define GNUTLS_BUFFERS_H
+
+#define MBUFFER_FLUSH 1
 
 int _gnutls_record_buffer_put (content_type_t type,
                               gnutls_session_t session, opaque * data,
@@ -37,7 +41,7 @@ void _gnutls_io_clear_read_buffer (gnutls_session_t);
 int _gnutls_io_clear_peeked_data (gnutls_session_t session);
 
 ssize_t _gnutls_io_write_buffered (gnutls_session_t session,
-                                  mbuffer_st *bufel);
+                                  mbuffer_st *bufel, unsigned int mflag);
 
 int _gnutls_handshake_buffer_get_size (gnutls_session_t session);
 int _gnutls_handshake_buffer_put (gnutls_session_t session, opaque * data,
@@ -54,8 +58,10 @@ int _gnutls_handshake_buffer_get_ptr (gnutls_session_t session,
 ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t, content_type_t,
                                       gnutls_handshake_description_t, void *,
                                       size_t);
-ssize_t _gnutls_handshake_io_send_int (gnutls_session_t,
+void _gnutls_handshake_io_cache_int (gnutls_session_t,
                                       gnutls_handshake_description_t,
                                       mbuffer_st *bufel);
 ssize_t _gnutls_io_write_flush (gnutls_session_t session);
 ssize_t _gnutls_handshake_io_write_flush (gnutls_session_t session);
+
+#endif
index 223abdca3b70be0e0922a14ddc68207086ce7f3f..943d2debd69bb9d2783227759d586a572186fddf 100644 (file)
@@ -269,45 +269,6 @@ gnutls_global_deinit (void)
  * historical reasons.
  */
 
-/**
- * gnutls_transport_set_pull_function:
- * @pull_func: a callback function similar to read()
- * @session: gnutls session
- *
- * This is the function where you set a function for gnutls to receive
- * data.  Normally, if you use berkeley style sockets, do not need to
- * use this function since the default (recv(2)) will probably be ok.
- *
- * PULL_FUNC is of the form,
- * ssize_t (*gnutls_pull_func)(gnutls_transport_ptr_t, void*, size_t);
- **/
-void
-gnutls_transport_set_pull_function (gnutls_session_t session,
-                                   gnutls_pull_func pull_func)
-{
-  session->internals._gnutls_pull_func = pull_func;
-}
-
-/**
- * gnutls_transport_set_push_function:
- * @push_func: a callback function similar to write()
- * @session: gnutls session
- *
- * This is the function where you set a push function for gnutls to
- * use in order to send data.  If you are going to use berkeley style
- * sockets, you do not need to use this function since the default
- * (send(2)) will probably be ok.  Otherwise you should specify this
- * function for gnutls to be able to send data.
- *
- * PUSH_FUNC is of the form,
- * ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t, const void*, size_t);
- **/
-void
-gnutls_transport_set_push_function (gnutls_session_t session,
-                                   gnutls_push_func push_func)
-{
-  session->internals._gnutls_push_func = push_func;
-}
 
 /**
  * gnutls_check_version:
index 790861a3fc8608ccb633704755c013066bf7ef93..780d5733a58d828d81f3fb5bbb535a4ea133ef30 100644 (file)
@@ -1162,7 +1162,30 @@ _gnutls_send_handshake (gnutls_session_t session, mbuffer_st *bufel,
 
   session->internals.last_handshake_out = type;
 
-  ret = _gnutls_handshake_io_send_int (session, type, bufel);
+  _gnutls_handshake_io_cache_int (session, type, bufel);
+    
+  switch (type) 
+    {
+      case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
+                                              * or ClientKeyExchange always.
+                                              */
+      case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: /* as above */
+      case GNUTLS_HANDSHAKE_SERVER_HELLO: /* as above */
+      case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* as above */
+      case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: /* followed by ChangeCipherSpec */
+
+      /* now for client Certificate, ClientKeyExchange and
+       * CertificateVerify are always followed by ChangeCipherSpec
+       */
+      case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
+      case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
+        ret = 0;
+        break;
+      default:
+        /* send cached messages */
+        ret = _gnutls_handshake_io_write_flush(session);
+        break;
+    }
 
   return ret;
 }
@@ -2307,12 +2330,25 @@ _gnutls_recv_hello (gnutls_session_t session, opaque * data, int datalen)
  *     CertificateVerify*
  *     [ChangeCipherSpec]
  *     Finished                     -------->
+ *                                                NewSessionTicket
  *                                              [ChangeCipherSpec]
  *                                  <--------             Finished
  *
  * (*): means optional packet.
  */
 
+/* Handshake when resumming session:
+ *      Client                                                Server
+ *
+ *      ClientHello                   -------->
+ *                                                      ServerHello
+ *                                               [ChangeCipherSpec]
+ *                                   <--------             Finished
+ *     [ChangeCipherSpec]
+ *     Finished                      -------->
+ * 
+ */
+
 /**
  * gnutls_rehandshake:
  * @session: is a #gnutls_session_t structure.
@@ -2731,6 +2767,7 @@ static int
 _gnutls_send_handshake_final (gnutls_session_t session, int init)
 {
   int ret = 0;
+  int oldstate = STATE;
 
   /* Send the CHANGE CIPHER SPEC PACKET */
 
@@ -2738,8 +2775,18 @@ _gnutls_send_handshake_final (gnutls_session_t session, int init)
     {
     case STATE0:
     case STATE20:
-      ret = _gnutls_send_change_cipher_spec (session, AGAIN (STATE20));
+
       STATE = STATE20;
+
+      ret = _gnutls_handshake_io_write_flush(session);
+      if (ret < 0) 
+        {
+          gnutls_assert();
+          return ret;
+        }
+
+      ret = _gnutls_send_change_cipher_spec (session, AGAIN2 (oldstate, STATE20));
+
       if (ret < 0)
        {
          ERR ("send ChangeCipherSpec", ret);
index 4203b1000be93956d27357d5df76bac2f37dd718..9a0ab6fe3038d00644b9578864bfe92862c20db7 100644 (file)
@@ -61,4 +61,5 @@ void _gnutls_handshake_hash_buffers_clear (gnutls_session_t session);
 /* This returns true if we have got there
  * before (and not finished due to an interrupt).
  */
-#define AGAIN(target) STATE==target?1:0
+#define AGAIN(target) (STATE==target?1:0)
+#define AGAIN2(state, target) (state==target?1:0)
index 47d96d1f0def2225549470b11f28928e6ad3c1c0..a7e32875e44a646634de0ffd311c6b90c1abc565 100644 (file)
@@ -586,8 +586,10 @@ typedef struct
 
   /* PUSH & PULL functions.
    */
-  gnutls_pull_func _gnutls_pull_func;
-  gnutls_push_func _gnutls_push_func;
+  gnutls_pull_func pull_func;
+  gnutls_push_func push_func;
+  gnutls_vec_push_func vec_push_func;
+  gnutls_errno_func errno_func;
   /* Holds the first argument of PUSH and PULL
    * functions;
    */
index cb360e38d6d5170eaa305ded3fda4cc97a282939..de0632489f909b466344fbac2c6a533fe7b75443 100644 (file)
@@ -63,14 +63,29 @@ _mbuffer_enqueue (mbuffer_head_st *buf, mbuffer_st *bufel)
   buf->tail = &bufel->next;
 }
 
-void
-_mbuffer_get_head (mbuffer_head_st *buf, gnutls_datum_t *msg)
+mbuffer_st* _mbuffer_get_first (mbuffer_head_st *buf, gnutls_datum_t *msg)
 {
-  mbuffer_st *bufel;
+  mbuffer_st *bufel = buf->head;
+
+  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;
+}
+
+mbuffer_st* _mbuffer_get_next (mbuffer_st * cur, gnutls_datum_t *msg)
+{
+  mbuffer_st *bufel = cur->next;
 
-  if (buf->head)
+  if (bufel)
     {
-      bufel = buf->head;
       msg->data = bufel->msg.data + bufel->mark;
       msg->size = bufel->msg.size - bufel->mark;
     }
@@ -79,6 +94,7 @@ _mbuffer_get_head (mbuffer_head_st *buf, gnutls_datum_t *msg)
       msg->data = NULL;
       msg->size = 0;
     }
+  return bufel;
 }
 
 static inline void
index 1ea67184fc172d944b729e2f10ce8304eb0f81cb..ed94824826ce1d6c3a919291f2145c4467773a72 100644 (file)
 void _mbuffer_init (mbuffer_head_st *buf);
 void _mbuffer_clear (mbuffer_head_st *buf);
 void _mbuffer_enqueue (mbuffer_head_st *buf, mbuffer_st *bufel);
-void _mbuffer_get_head (mbuffer_head_st *buf, gnutls_datum_t *msg);
 int  _mbuffer_remove_bytes (mbuffer_head_st *buf, size_t bytes);
 mbuffer_st* _mbuffer_alloc (size_t payload_size, size_t maximum_size);
 
+mbuffer_st* _mbuffer_get_first (mbuffer_head_st *buf, gnutls_datum_t *msg);
+mbuffer_st* _mbuffer_get_next (mbuffer_st * cur, gnutls_datum_t *msg);
+
 /* This is dangerous since it will replace bufel with a new
  * one.
  */
index 9ea214e25780eaadc4a7d4dc798dee9b547faec6..a10a6fd6a78d3d01ee45babd7d2376d2df0be504 100644 (file)
@@ -335,7 +335,7 @@ copy_record_version (gnutls_session_t session,
 ssize_t
 _gnutls_send_int (gnutls_session_t session, content_type_t type,
                  gnutls_handshake_description_t htype, const void *_data,
-                 size_t sizeofdata)
+                 size_t sizeofdata, unsigned int mflags)
 {
   mbuffer_st *bufel;
   size_t cipher_size;
@@ -384,7 +384,7 @@ _gnutls_send_int (gnutls_session_t session, content_type_t type,
   /* Only encrypt if we don't have data to send 
    * from the previous run. - probably interrupted.
    */
-  if (session->internals.record_send_buffer.byte_length > 0)
+  if (mflags != 0 && session->internals.record_send_buffer.byte_length > 0)
     {
       ret = _gnutls_io_write_flush (session);
       if (ret > 0)
@@ -435,7 +435,7 @@ _gnutls_send_int (gnutls_session_t session, content_type_t type,
        }
 
       _mbuffer_set_udata_size(bufel, cipher_size);
-      ret = _gnutls_io_write_buffered (session, bufel);
+      ret = _gnutls_io_write_buffered (session, bufel, mflags);
     }
 
   if (ret != cipher_size)
@@ -481,7 +481,7 @@ _gnutls_send_change_cipher_spec (gnutls_session_t session, int again)
   _gnutls_handshake_log ("REC[%p]: Sent ChangeCipherSpec\n", session);
 
   if (again == 0)
-    return _gnutls_send_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, data, 1);
+    return _gnutls_send_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, data, 1, MBUFFER_FLUSH);
   else
     {
       return _gnutls_io_write_flush (session);
@@ -1133,7 +1133,7 @@ gnutls_record_send (gnutls_session_t session, const void *data,
                    size_t sizeofdata)
 {
   return _gnutls_send_int (session, GNUTLS_APPLICATION_DATA, -1, data,
-                          sizeofdata);
+                          sizeofdata, MBUFFER_FLUSH);
 }
 
 /**
index bcb991f30c678c7f178def10023053dcf14587a4..6d35247cca269e16abe11938d8603d7c80d27488 100644 (file)
  *
  */
 
+#ifndef GNUTLS_RECORD_H
+# define GNUTLS_RECORD_H
+
+# include <gnutls/gnutls.h>
+# include <gnutls_buffers.h>
+
 ssize_t _gnutls_send_int (gnutls_session_t session, content_type_t type,
                          gnutls_handshake_description_t htype,
-                         const void *data, size_t sizeofdata);
+                         const void *data, size_t sizeofdata, unsigned int mflags);
 ssize_t _gnutls_recv_int (gnutls_session_t session, content_type_t type,
                          gnutls_handshake_description_t, opaque * data,
                          size_t sizeofdata);
 ssize_t _gnutls_send_change_cipher_spec (gnutls_session_t session, int again);
+
+#endif
index b5769de176dea9ce0d546fceba8ece9538151cef..2adfa2471fb5da64ab4bb4a631919eed79800323 100644 (file)
@@ -47,6 +47,8 @@
 #include <gnutls_rsa_export.h>
 #include <gnutls_extensions.h>
 
+#include <errno.h>
+
 /* These should really be static, but src/tests.c calls them.  Make
    them public functions?  */
 void
@@ -246,6 +248,53 @@ _gnutls_handshake_internal_state_clear (gnutls_session_t session)
 
 }
 
+/* wrappers for write() and writev()
+ */
+#ifdef _WIN32
+
+static int system_errno(gnutls_transport_ptr)
+{
+  int tmperr = WSAGetLastError ();
+  int ret = 0;
+  switch (tmperr)
+    {
+        case WSAEWOULDBLOCK:
+          ret = EAGAIN;
+          break;
+        case WSAEINTR:
+          ret = EINTR;
+          break;
+        default:
+          ret = EIO;
+          break;
+     }
+   WSASetLastError (tmperr);
+
+  return ret;
+}
+
+static ssize_t system_write(gnutls_transport_ptr ptr, const void* data, size_t data_size)
+{
+  return send( GNUTLS_POINTER_TO_INT(ptr), data, data_size, 0);
+}
+#else /* POSIX */
+static int system_errno(gnutls_transport_ptr ptr)
+{
+  return errno;
+}
+
+static ssize_t system_writev(gnutls_transport_ptr ptr, const giovec_t * iovec, int iovec_cnt)
+{
+  return writev(GNUTLS_POINTER_TO_INT(ptr), (struct iovec*) iovec, iovec_cnt);
+
+}
+#endif
+
+static ssize_t system_read(gnutls_transport_ptr ptr, void* data, size_t data_size)
+{
+  return recv( GNUTLS_POINTER_TO_INT(ptr), data, data_size, 0);
+}
+
 #define MIN_DH_BITS 727
 /**
  * gnutls_init:
@@ -352,6 +401,14 @@ gnutls_init (gnutls_session_t * session, gnutls_connection_end_t con_end)
    */
   (*session)->internals.priorities.sr = SR_PARTIAL;
 
+#ifdef _WIN32
+  gnutls_transport_set_push_function(*session, system_write);
+#else
+  gnutls_transport_set_push_function2(*session, system_writev);
+#endif
+  gnutls_transport_set_pull_function(*session, system_read);
+  gnutls_transport_set_errno_function(*session, system_errno);
+
   return 0;
 }
 
index 95554320a2280f06f5319b124941ab3d607444f0..6bed294323e33f8f7bce8f22d989a7c733417cf1 100644 (file)
@@ -1209,10 +1209,19 @@ extern "C"
 
 /* Session stuff
  */
+  typedef struct {
+    void  *iov_base;    /* Starting address */
+    size_t iov_len;     /* Number of bytes to transfer */
+  } giovec_t;
+
   typedef ssize_t (*gnutls_pull_func) (gnutls_transport_ptr_t, void *,
                                       size_t);
-  typedef ssize_t (*gnutls_push_func) (gnutls_transport_ptr_t, const void *,
-                                      size_t);
+  typedef ssize_t (*gnutls_push_func) (gnutls_transport_ptr_t, const void *, size_t);
+
+  typedef ssize_t (*gnutls_vec_push_func) (gnutls_transport_ptr_t, const giovec_t *iov, int iovcnt);
+
+  typedef int (*gnutls_errno_func) (gnutls_transport_ptr_t);
+
   void gnutls_transport_set_ptr (gnutls_session_t session,
                                 gnutls_transport_ptr_t ptr);
   void gnutls_transport_set_ptr2 (gnutls_session_t session,
@@ -1227,11 +1236,16 @@ extern "C"
   void gnutls_transport_set_lowat (gnutls_session_t session, int num);
 
 
+  void gnutls_transport_set_push_function2 (gnutls_session_t session,
+                                      gnutls_vec_push_func vec_func);
   void gnutls_transport_set_push_function (gnutls_session_t session,
                                           gnutls_push_func push_func);
   void gnutls_transport_set_pull_function (gnutls_session_t session,
                                           gnutls_pull_func pull_func);
 
+  void gnutls_transport_set_errno_function (gnutls_session_t session,
+                                          gnutls_errno_func errno_func);
+
   void gnutls_transport_set_errno (gnutls_session_t session, int err);
   void gnutls_transport_set_global_errno (int err);
 
index 9b25b299e0c0e8811755763ac9c1879a1341e525..88c5c25234828dd2937a4fd22e5db202951f3327 100644 (file)
@@ -675,6 +675,8 @@ GNUTLS_2_11
        gnutls_pubkey_get_preferred_hash_algorithm;
        gnutls_x509_crt_get_preferred_hash_algorithm;
        gnutls_global_set_mutex;
+        gnutls_transport_set_push_function2;
+        gnutls_transport_set_errno_function;
 
        gnutls_sec_param_to_pk_bits;
        gnutls_sec_param_get_name;
index f3f1e4a9a7ee2f188fbe986b2e92ad396b55d712..e2ff0d9e131dd90f3288fa7f2e0e2aa3bee933f2 100644 (file)
@@ -97,7 +97,7 @@ _gnutls_send_inner_application (gnutls_session_t session,
       memcpy (p + 4, data, sizeofdata);
     }
 
-  len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen);
+  len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen, MBUFFER_FLUSH);
 
   if (p)
     gnutls_free (p);