Please review / test this patch.

BTW, I am not sure if contributors.debian.org is configured correctly.
The rehandshake occurs right after the HTTP request and it has a pretty heavy  
impact on download duration.

Regards, Tim

On Sunday 27 September 2015 20:03:54 Tim Ruehsen wrote:
> Follow-up Comment #2, bug #46061 (project wget):
> 
> Wget is not reacting on GNUTLS_E_REHANDSHAKE. Should be straight forward...
> 
> 
>     _______________________________________________________
> 
> Reply to this item at:
> 
>   <http://savannah.gnu.org/bugs/?46061>
> 
> _______________________________________________
>   Nachricht gesendet von/durch Savannah
>   http://savannah.gnu.org/
>From cbec5b0c780f9d1fc343fabf22e8ee7c7cb3222d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20R=C3=BChsen?= <tim.rueh...@gmx.de>
Date: Mon, 28 Sep 2015 12:00:33 +0200
Subject: [PATCH] Handle TLS rehandshakes in GnuTLS code

* src/gnutls.c: New static function _do_handshake()
* src/gnutls.c (wgnutls_read_timeout): Handle rehandshake
* src/gnutls.c (wgnutls_write): Handle rehandshake
* src/gnutls.c (ssl_connect_wget): Move handshake code into _do_handshake()

Fixes #46061
---
 src/gnutls.c | 179 ++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 102 insertions(+), 77 deletions(-)

diff --git a/src/gnutls.c b/src/gnutls.c
index a38301a..2f53592 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -57,6 +57,9 @@ as that of the covered work.  */
 #include "host.h"
 
 static int
+_do_handshake(gnutls_session_t session, int fd, double timeout);
+
+static int
 key_type_to_gnutls_type (enum keyfile_type type)
 {
   switch (type)
@@ -277,6 +280,12 @@ wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
         {
           ret = gnutls_record_recv (ctx->session, buf, bufsize);
           timed_out = timeout && ptimer_measure (timer) >= timeout;
+          if (!timed_out && ret == GNUTLS_E_REHANDSHAKE)
+            {
+              DEBUGP (("GnuTLS: *** REHANDSHAKE while reading\n"));
+              if ((ret = _do_handshake(ctx->session, fd, timeout)) == 0)
+                ret = GNUTLS_E_AGAIN; /* restart reading */
+            }
         }
     }
   while (ret == GNUTLS_E_INTERRUPTED || (ret == GNUTLS_E_AGAIN && !timed_out));
@@ -332,7 +341,15 @@ wgnutls_write (int fd _GL_UNUSED, char *buf, int bufsize, void *arg)
   int ret;
   struct wgnutls_transport_context *ctx = arg;
   do
-    ret = gnutls_record_send (ctx->session, buf, bufsize);
+    {
+      ret = gnutls_record_send (ctx->session, buf, bufsize);
+      if (ret == GNUTLS_E_REHANDSHAKE)
+        {
+          DEBUGP (("GnuTLS: *** REHANDSHAKE while writing\n"));
+          if ((ret = _do_handshake(ctx->session, fd, 0)) == 0)
+            ret = GNUTLS_E_AGAIN; /* restart writing */
+        }
+    }
   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
   if (ret < 0)
     ctx->last_error = ret;
@@ -425,16 +442,96 @@ static struct transport_implementation wgnutls_transport =
   wgnutls_peek, wgnutls_errstr, wgnutls_close
 };
 
-bool
-ssl_connect_wget (int fd, const char *hostname, int *continue_session)
+static int
+_do_handshake(gnutls_session_t session, int fd, double timeout)
 {
 #ifdef F_GETFL
   int flags = 0;
 #endif
+  int err;
+
+  if (timeout)
+    {
+#ifdef F_GETFL
+      flags = fcntl (fd, F_GETFL, 0);
+      if (flags < 0)
+        return flags;
+      if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
+        return -1;
+#else
+      /* XXX: Assume it was blocking before.  */
+      const int one = 1;
+      if (ioctl (fd, FIONBIO, &one) < 0)
+        return -1;
+#endif
+    }
+
+  /* We don't stop the handshake process for non-fatal errors */
+  do
+    {
+      err = gnutls_handshake (session);
+
+      if (timeout && err == GNUTLS_E_AGAIN)
+        {
+          if (gnutls_record_get_direction (session))
+            {
+              /* wait for writeability */
+              err = select_fd (fd, timeout, WAIT_FOR_WRITE);
+            }
+          else
+            {
+              /* wait for readability */
+              err = select_fd (fd, timeout, WAIT_FOR_READ);
+            }
+
+          if (err <= 0)
+            {
+              if (err == 0)
+                {
+                  errno = ETIMEDOUT;
+                  err = -1;
+                }
+              break;
+            }
+
+           err = GNUTLS_E_AGAIN;
+        }
+      else if (err < 0)
+        {
+          logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
+          if (err == GNUTLS_E_WARNING_ALERT_RECEIVED ||
+              err == GNUTLS_E_FATAL_ALERT_RECEIVED)
+            {
+              gnutls_alert_description_t alert = gnutls_alert_get (session);
+              const char *str = gnutls_alert_get_name (alert);
+              logprintf (LOG_NOTQUIET, "GnuTLS: received alert [%d]: %s\n",
+                         alert, str ? str : "(unknown)");
+            }
+        }
+    }
+  while (err && gnutls_error_is_fatal (err) == 0);
+
+  if (timeout)
+    {
+#ifdef F_GETFL
+      if (fcntl (fd, F_SETFL, flags) < 0)
+        return -1;
+#else
+      const int zero = 0;
+      if (ioctl (fd, FIONBIO, &zero) < 0)
+        return -1;
+#endif
+    }
+
+  return err;
+}
+
+bool
+ssl_connect_wget (int fd, const char *hostname, int *continue_session)
+{
   struct wgnutls_transport_context *ctx;
   gnutls_session_t session;
   int err;
-  const char *str;
 
   gnutls_init (&session, GNUTLS_CLIENT);
 
@@ -558,79 +655,7 @@ ssl_connect_wget (int fd, const char *hostname, int *continue_session)
         }
     }
 
-  if (opt.connect_timeout)
-    {
-#ifdef F_GETFL
-      flags = fcntl (fd, F_GETFL, 0);
-      if (flags < 0)
-        return flags;
-      if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
-        return -1;
-#else
-      /* XXX: Assume it was blocking before.  */
-      const int one = 1;
-      if (ioctl (fd, FIONBIO, &one) < 0)
-        return -1;
-#endif
-    }
-
-  /* We don't stop the handshake process for non-fatal errors */
-  do
-    {
-      err = gnutls_handshake (session);
-
-      if (opt.connect_timeout && err == GNUTLS_E_AGAIN)
-        {
-          if (gnutls_record_get_direction (session))
-            {
-              /* wait for writeability */
-              err = select_fd (fd, opt.connect_timeout, WAIT_FOR_WRITE);
-            }
-          else
-            {
-              /* wait for readability */
-              err = select_fd (fd, opt.connect_timeout, WAIT_FOR_READ);
-            }
-
-          if (err <= 0)
-            {
-              if (err == 0)
-                {
-                  errno = ETIMEDOUT;
-                  err = -1;
-                }
-              break;
-            }
-
-           err = GNUTLS_E_AGAIN;
-        }
-      else if (err < 0)
-        {
-          logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
-          if (err == GNUTLS_E_WARNING_ALERT_RECEIVED ||
-              err == GNUTLS_E_FATAL_ALERT_RECEIVED)
-            {
-              gnutls_alert_description_t alert = gnutls_alert_get (session);
-              str = gnutls_alert_get_name (alert);
-              if (str == NULL)
-                str = "(unknown)";
-              logprintf (LOG_NOTQUIET, "GnuTLS: received alert [%d]: %s\n", alert, str);
-            }
-        }
-    }
-  while (err && gnutls_error_is_fatal (err) == 0);
-
-  if (opt.connect_timeout)
-    {
-#ifdef F_GETFL
-      if (fcntl (fd, F_SETFL, flags) < 0)
-        return -1;
-#else
-      const int zero = 0;
-      if (ioctl (fd, FIONBIO, &zero) < 0)
-        return -1;
-#endif
-    }
+  err = _do_handshake(session, fd, opt.connect_timeout);
 
   if (err < 0)
     {
-- 
2.5.3

Reply via email to