Dima Barsky reported a problem with GnuTLS-enabled libcurl in bug report
authorDaniel Stenberg <daniel@haxx.se>
Sat, 22 Oct 2005 21:05:07 +0000 (21:05 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 22 Oct 2005 21:05:07 +0000 (21:05 +0000)
  #1334338 (http://curl.haxx.se/bug/view.cgi?id=1334338). When reading an SSL
  stream from a server and the server requests a "rehandshake", the current
  code simply returns this as an error. I have no good way to test this, but
  I've added a crude attempt of dealing with this situation slightly better -
  it makes a blocking handshake if this happens. Done like this because fixing
  this the "proper" way (that would handshake asynchronously) will require
  quite some work and I really need a good way to test this to do such a
  change.

CHANGES
lib/gtls.c

diff --git a/CHANGES b/CHANGES
index 0fd2fe2..267ae2f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,17 @@
 
 
 
+Daniel (22 October 2005)
+- Dima Barsky reported a problem with GnuTLS-enabled libcurl in bug report
+  #1334338 (http://curl.haxx.se/bug/view.cgi?id=1334338). When reading an SSL
+  stream from a server and the server requests a "rehandshake", the current
+  code simply returns this as an error. I have no good way to test this, but
+  I've added a crude attempt of dealing with this situation slightly better -
+  it makes a blocking handshake if this happens. Done like this because fixing
+  this the "proper" way (that would handshake asynchronously) will require
+  quite some work and I really need a good way to test this to do such a
+  change.
+
 Daniel (21 October 2005)
 - "Ofer" reported a problem when libcurl re-used a connection and failed to do
   it, it could then accidentally actually crash. Presumably, this concerns FTP
index dbe3d1f..aa9d98d 100644 (file)
@@ -110,6 +110,72 @@ static void showtime(struct SessionHandle *data,
   infof(data, "%s", data->state.buffer);
 }
 
+/* this function does a BLOCKING SSL/TLS (re-)handshake */
+static CURLcode handshake(struct connectdata *conn,
+                          gnutls_session session,
+                          int sockindex,
+                          bool duringconnect)
+{
+  struct SessionHandle *data = conn->data;
+  int rc;
+
+  do {
+    rc = gnutls_handshake(session);
+
+    if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
+      long timeout_ms = DEFAULT_CONNECT_TIMEOUT;
+      long has_passed;
+
+      if(duringconnect && data->set.connecttimeout)
+        timeout_ms = data->set.connecttimeout*1000;
+
+      if(data->set.timeout) {
+        /* get the strictest timeout of the ones converted to milliseconds */
+        if((data->set.timeout*1000) < timeout_ms)
+          timeout_ms = data->set.timeout*1000;
+      }
+
+      /* Evaluate in milliseconds how much time that has passed */
+      has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
+
+      /* subtract the passed time */
+      timeout_ms -= has_passed;
+
+      if(timeout_ms < 0) {
+        /* a precaution, no need to continue if time already is up */
+        failf(data, "SSL connection timeout");
+        return CURLE_OPERATION_TIMEOUTED;
+      }
+
+      rc = Curl_select(conn->sock[sockindex],
+                       conn->sock[sockindex], (int)timeout_ms);
+      if(rc > 0)
+        /* reabable or writable, go loop*/
+        continue;
+      else if(0 == rc) {
+        /* timeout */
+        failf(data, "SSL connection timeout");
+        return CURLE_OPERATION_TIMEDOUT;
+      }
+      else {
+        /* anything that gets here is fatally bad */
+        failf(data, "select on SSL socket, errno: %d", Curl_ourerrno());
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+    }
+    else
+      break;
+  } while(1);
+
+  if (rc < 0) {
+    failf(data, "gnutls_handshake() failed: %d", rc);
+    /* gnutls_perror(ret); */
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  return CURLE_OK;
+}
+
 /*
  * This function is called after the TCP connect has completed. Setup the TLS
  * layer and do all necessary magic.
@@ -206,61 +272,10 @@ Curl_gtls_connect(struct connectdata *conn,
     infof (data, "SSL re-using session ID\n");
   }
 
-  do {
-    rc = gnutls_handshake(session);
-
-    if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
-      long timeout_ms;
-      long has_passed;
-
-      if(data->set.timeout || data->set.connecttimeout) {
-        /* get the most strict timeout of the ones converted to milliseconds */
-        if(data->set.timeout &&
-           (data->set.timeout>data->set.connecttimeout))
-          timeout_ms = data->set.timeout*1000;
-        else
-          timeout_ms = data->set.connecttimeout*1000;
-      }
-      else
-        timeout_ms = DEFAULT_CONNECT_TIMEOUT;
-
-      /* Evaluate in milliseconds how much time that has passed */
-      has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
-
-      /* subtract the passed time */
-      timeout_ms -= has_passed;
-
-      if(timeout_ms < 0) {
-        /* a precaution, no need to continue if time already is up */
-        failf(data, "SSL connection timeout");
-        return CURLE_OPERATION_TIMEOUTED;
-      }
-
-      rc = Curl_select(conn->sock[sockindex],
-                         conn->sock[sockindex], (int)timeout_ms);
-      if(rc > 0)
-        /* reabable or writable, go loop*/
-        continue;
-      else if(0 == rc) {
-        /* timeout */
-        failf(data, "SSL connection timeout");
-        return CURLE_OPERATION_TIMEDOUT;
-      }
-      else {
-        /* anything that gets here is fatally bad */
-        failf(data, "select on SSL socket, errno: %d", Curl_ourerrno());
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-    }
-    else
-      break;
-  } while(1);
-
-  if (rc < 0) {
-    failf(data, "gnutls_handshake() failed: %d", rc);
-    /* gnutls_perror(ret); */
-    return CURLE_SSL_CONNECT_ERROR;
-  }
+  rc = handshake(conn, session, sockindex, TRUE);
+  if(rc)
+    /* handshake() sets its own error message with failf() */
+    return rc;
 
   /* This function will return the peer's raw certificate (chain) as sent by
      the peer. These certificates are in raw format (DER encoded for
@@ -467,6 +482,17 @@ ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */
     return -1;
   }
 
+  if(ret == GNUTLS_E_REHANDSHAKE) {
+    /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
+       proper way" takes a whole lot of work. */
+    CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE);
+    if(rc)
+      /* handshake() writes error message on its own */
+      return rc;
+    *wouldblock = TRUE; /* then return as if this was a wouldblock */
+    return -1;
+  }
+
   *wouldblock = FALSE;
   if (!ret) {
     failf(conn->data, "Peer closed the TLS connection");