From 0db886879c22453e3c82520391dceb4357bb15d8 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <gscrivan@redhat.com>
Date: Mon, 28 Sep 2015 13:04:49 +0200
Subject: [PATCH] gnutls: honor error GNUTLS_E_REHANDSHAKE

* src/gnutls.c (do_handshake): New function.
(wgnutls_read_timeout): Repeat the handshake on GNUTLS_E_REHANDSHAKE.
(ssl_connect_wget): Move handshake code to 'do_handshake'.
---
 src/gnutls.c | 110 ++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 64 insertions(+), 46 deletions(-)

diff --git a/src/gnutls.c b/src/gnutls.c
index a38301a..c5beec9 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -231,6 +231,58 @@ struct wgnutls_transport_context
 };
 
 static int
+do_handshake (int fd, gnutls_session_t session, double timeout)
+{
+  const char *str;
+  int err;
+  /* 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);
+              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);
+
+  return err;
+}
+
+static int
 wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
 {
 #ifdef F_GETFL
@@ -276,6 +328,17 @@ wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
           || select_fd (fd, next_timeout, WAIT_FOR_READ))
         {
           ret = gnutls_record_recv (ctx->session, buf, bufsize);
+          if (ret == GNUTLS_E_REHANDSHAKE)
+            {
+              int hs_ret = do_handshake (fd, ctx->session, next_timeout);
+              if (hs_ret)
+                {
+                  ret = hs_ret;
+                  break;
+                }
+              ret = GNUTLS_E_AGAIN;
+            }
+
           timed_out = timeout && ptimer_measure (timer) >= timeout;
         }
     }
@@ -434,7 +497,6 @@ 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);
 
@@ -574,51 +636,7 @@ ssl_connect_wget (int fd, const char *hostname, int *continue_session)
 #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);
+  err = do_handshake (fd, session, opt.connect_timeout);
 
   if (opt.connect_timeout)
     {
-- 
2.4.3

