]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
vtls: new io_need flags for poll handling
authorStefan Eissing <stefan@eissing.org>
Tue, 4 Jun 2024 12:00:12 +0000 (14:00 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 5 Jun 2024 07:03:38 +0000 (09:03 +0200)
- decouple need to recv/send from negotiation state, we need
  this later in shutdown handling as well
- move ssl enums from urldata.h to vtls_int.h
- implement use of `connssl->io_need` in vtls.c. and all backends

Closes #13879

lib/urldata.h
lib/vtls/bearssl.c
lib/vtls/gtls.c
lib/vtls/mbedtls.c
lib/vtls/openssl.c
lib/vtls/rustls.c
lib/vtls/schannel.c
lib/vtls/sectransp.c
lib/vtls/vtls.c
lib/vtls/vtls_int.h
lib/vtls/wolfssl.c

index 15eb1f8364865216f420aca541d74a2df4c678b1..df2de388d952563767c26bdeec688bf3cb980271 100644 (file)
@@ -257,22 +257,6 @@ enum protection_level {
 };
 #endif
 
-/* enum for the nonblocking SSL connection state machine */
-typedef enum {
-  ssl_connect_1,
-  ssl_connect_2,
-  ssl_connect_2_reading,
-  ssl_connect_2_writing,
-  ssl_connect_3,
-  ssl_connect_done
-} ssl_connect_state;
-
-typedef enum {
-  ssl_connection_none,
-  ssl_connection_negotiating,
-  ssl_connection_complete
-} ssl_connection_state;
-
 /* SSL backend-specific data; declared differently by each SSL backend */
 struct ssl_backend_data;
 
index dd59780c8670b10a540ede2969e6da8debec68d5..b62be48e8b0b4e63f0d3aaf4dac75cb81e1fb63c 100644 (file)
@@ -722,6 +722,8 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
       ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result);
       CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
       if(ret <= 0) {
+        if(result == CURLE_AGAIN)
+          connssl->io_need |= CURL_SSL_IO_NEED_SEND;
         return result;
       }
       br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret);
@@ -735,6 +737,8 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
         return CURLE_RECV_ERROR;
       }
       if(ret <= 0) {
+        if(result == CURLE_AGAIN)
+          connssl->io_need |= CURL_SSL_IO_NEED_RECV;
         return result;
       }
       br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret);
@@ -925,9 +929,7 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
       return ret;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
+  while(ssl_connect_2 == connssl->connecting_state) {
     /* check allowed time left */
     timeout_ms = Curl_timeleft(data, NULL, TRUE);
 
@@ -938,13 +940,12 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
     }
 
     /* if ssl is expecting something, check if it's available. */
-    if(ssl_connect_2_reading == connssl->connecting_state ||
-       ssl_connect_2_writing == connssl->connecting_state) {
+    if(connssl->io_need) {
 
-      curl_socket_t writefd = ssl_connect_2_writing ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+                              sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+                             sockfd:CURL_SOCKET_BAD;
 
       CURL_TRC_CF(data, cf, "connect_common, check socket");
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
@@ -975,11 +976,9 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
      * before step2 has completed while ensuring that a client using select()
      * or epoll() will always have a valid fdset to wait on.
      */
+    connssl->io_need = CURL_SSL_IO_NEED_NONE;
     ret = bearssl_connect_step2(cf, data);
-    if(ret || (nonblocking &&
-               (ssl_connect_2 == connssl->connecting_state ||
-                ssl_connect_2_reading == connssl->connecting_state ||
-                ssl_connect_2_writing == connssl->connecting_state)))
+    if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
       return ret;
   }
 
index 9038739d4ecaea631b9d5428f0b34253e6c451c0..a828ce22ee38e8a0315d959ca004fd5164a11c89 100644 (file)
@@ -251,6 +251,7 @@ static CURLcode handshake(struct Curl_cfilter *cf,
 
   DEBUGASSERT(backend);
   session = backend->gtls.session;
+  connssl->connecting_state = ssl_connect_2;
 
   for(;;) {
     timediff_t timeout_ms;
@@ -266,13 +267,12 @@ static CURLcode handshake(struct Curl_cfilter *cf,
     }
 
     /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-       || connssl->connecting_state == ssl_connect_2_writing) {
+    if(connssl->io_need) {
       int what;
-      curl_socket_t writefd = ssl_connect_2_writing ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+                              sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+                             sockfd:CURL_SOCKET_BAD;
 
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
                                nonblocking?0:
@@ -294,6 +294,7 @@ static CURLcode handshake(struct Curl_cfilter *cf,
       /* socket is readable or writable */
     }
 
+    connssl->io_need = CURL_SSL_IO_NEED_NONE;
     backend->gtls.io_result = CURLE_OK;
     rc = gnutls_handshake(session);
 
@@ -306,9 +307,9 @@ static CURLcode handshake(struct Curl_cfilter *cf,
     }
 
     if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
-      connssl->connecting_state =
+      connssl->io_need =
         gnutls_record_get_direction(session)?
-        ssl_connect_2_writing:ssl_connect_2_reading;
+        CURL_SSL_IO_NEED_SEND:CURL_SSL_IO_NEED_RECV;
       continue;
     }
     else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
@@ -1684,8 +1685,8 @@ out:
  */
 /* We use connssl->connecting_state to keep track of the connection status;
    there are three states: 'ssl_connect_1' (not started yet or complete),
-   'ssl_connect_2_reading' (waiting for data from server), and
-   'ssl_connect_2_writing' (waiting to be able to write).
+   'ssl_connect_2' (doing handshake with the server), and
+   'ssl_connect_3' (verifying and getting stats).
  */
 static CURLcode
 gtls_connect_common(struct Curl_cfilter *cf,
index 8393b02d2f7a13f38e92a32ef81d6aef0d1758f4..b71af28055835c21a37a84fbd4e21a8af1b01abd 100644 (file)
@@ -943,11 +943,11 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
   ret = mbedtls_ssl_handshake(&backend->ssl);
 
   if(ret == MBEDTLS_ERR_SSL_WANT_READ) {
-    connssl->connecting_state = ssl_connect_2_reading;
+    connssl->io_need = CURL_SSL_IO_NEED_RECV;
     return CURLE_OK;
   }
   else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
-    connssl->connecting_state = ssl_connect_2_writing;
+    connssl->io_need = CURL_SSL_IO_NEED_SEND;
     return CURLE_OK;
   }
   else if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
@@ -1358,9 +1358,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
       return retcode;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
+  while(ssl_connect_2 == connssl->connecting_state) {
 
     /* check allowed time left */
     timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -1372,13 +1370,12 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
     }
 
     /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-       || connssl->connecting_state == ssl_connect_2_writing) {
+    if(connssl->io_need) {
 
-      curl_socket_t writefd = ssl_connect_2_writing ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+                              sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+                             sockfd:CURL_SOCKET_BAD;
 
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
                                nonblocking ? 0 : timeout_ms);
@@ -1408,11 +1405,10 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
      * ensuring that a client using select() or epoll() will always
      * have a valid fdset to wait on.
      */
+    connssl->io_need = CURL_SSL_IO_NEED_NONE;
     retcode = mbed_connect_step2(cf, data);
-    if(retcode || (nonblocking &&
-                   (ssl_connect_2 == connssl->connecting_state ||
-                    ssl_connect_2_reading == connssl->connecting_state ||
-                    ssl_connect_2_writing == connssl->connecting_state)))
+    if(retcode ||
+       (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
       return retcode;
 
   } /* repeat step2 until all transactions are done. */
index bc6a10f96c06e7205b6cd4afd375ce278c011cfe..f6346bbf03d4cace744f52c1316df9a8c547a7a7 100644 (file)
@@ -4165,11 +4165,10 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
   struct ssl_connect_data *connssl = cf->ctx;
   struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
-              || ssl_connect_2_reading == connssl->connecting_state
-              || ssl_connect_2_writing == connssl->connecting_state);
+  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state);
   DEBUGASSERT(octx);
 
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
   ERR_clear_error();
 
   err = SSL_connect(octx->ssl);
@@ -4198,11 +4197,11 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
     int detail = SSL_get_error(octx->ssl, err);
 
     if(SSL_ERROR_WANT_READ == detail) {
-      connssl->connecting_state = ssl_connect_2_reading;
+      connssl->io_need = CURL_SSL_IO_NEED_RECV;
       return CURLE_OK;
     }
     if(SSL_ERROR_WANT_WRITE == detail) {
-      connssl->connecting_state = ssl_connect_2_writing;
+      connssl->io_need = CURL_SSL_IO_NEED_SEND;
       return CURLE_OK;
     }
 #ifdef SSL_ERROR_WANT_ASYNC
@@ -4828,9 +4827,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
       goto out;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
+  while(ssl_connect_2 == connssl->connecting_state) {
 
     /* check allowed time left */
     const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -4843,14 +4840,12 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
     }
 
     /* if ssl is expecting something, check if it's available. */
-    if(!nonblocking &&
-       (connssl->connecting_state == ssl_connect_2_reading ||
-        connssl->connecting_state == ssl_connect_2_writing)) {
+    if(!nonblocking && connssl->io_need) {
 
-      curl_socket_t writefd = ssl_connect_2_writing ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+                              sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+                             sockfd:CURL_SOCKET_BAD;
 
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
                                timeout_ms);
@@ -4876,10 +4871,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
      * or epoll() will always have a valid fdset to wait on.
      */
     result = ossl_connect_step2(cf, data);
-    if(result || (nonblocking &&
-                  (ssl_connect_2 == connssl->connecting_state ||
-                   ssl_connect_2_reading == connssl->connecting_state ||
-                   ssl_connect_2_writing == connssl->connecting_state)))
+    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
       goto out;
 
   } /* repeat step2 until all transactions are done. */
index d666f1b60a9eed668829e2f39811d409a75d3f00..722797c2b97c145e266a07079d25f4dad5da5896 100644 (file)
@@ -604,6 +604,7 @@ cr_connect_common(struct Curl_cfilter *cf,
     * Connection has been established according to rustls. Set send/recv
     * handlers, and update the state machine.
     */
+    connssl->io_need = CURL_SSL_IO_NEED_NONE;
     if(!rustls_connection_is_handshaking(rconn)) {
       infof(data, "Done handshaking");
       /* rustls claims it is no longer handshaking *before* it has
@@ -613,7 +614,7 @@ cr_connect_common(struct Curl_cfilter *cf,
       cr_set_negotiated_alpn(cf, data, rconn);
       cr_send(cf, data, NULL, 0, &tmperr);
       if(tmperr == CURLE_AGAIN) {
-        connssl->connecting_state = ssl_connect_2_writing;
+        connssl->io_need = CURL_SSL_IO_NEED_SEND;
         return CURLE_OK;
       }
       else if(tmperr != CURLE_OK) {
@@ -625,6 +626,7 @@ cr_connect_common(struct Curl_cfilter *cf,
       return CURLE_OK;
     }
 
+    connssl->connecting_state = ssl_connect_2;
     wants_read = rustls_connection_wants_read(rconn);
     wants_write = rustls_connection_wants_write(rconn) ||
                   backend->plain_out_buffered;
@@ -632,8 +634,6 @@ cr_connect_common(struct Curl_cfilter *cf,
     writefd = wants_write?sockfd:CURL_SOCKET_BAD;
     readfd = wants_read?sockfd:CURL_SOCKET_BAD;
 
-    connssl->connecting_state = wants_write?
-      ssl_connect_2_writing : ssl_connect_2_reading;
     /* check allowed time left */
     timeout_ms = Curl_timeleft(data, NULL, TRUE);
 
@@ -661,6 +661,10 @@ cr_connect_common(struct Curl_cfilter *cf,
       CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block",
             wants_read&&wants_write ? "writing and reading" :
             wants_write ? "writing" : "reading");
+      if(wants_write)
+        connssl->io_need |= CURL_SSL_IO_NEED_SEND;
+      if(wants_read)
+        connssl->io_need |= CURL_SSL_IO_NEED_RECV;
       return CURLE_OK;
     }
     /* socket is readable or writable */
index 57d0c33d649eaa2d64b92ac1aaeef5c527140af8..02e59d8469ba995124bad31dbc430597aca91d8b 100644 (file)
@@ -1332,7 +1332,8 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   DEBUGASSERT(backend);
 
-  doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
+  doread = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? FALSE : TRUE;
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
 
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %d (step 2/3)",
@@ -1393,8 +1394,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
                                backend->encdata_offset,
                                &result);
       if(result == CURLE_AGAIN) {
-        if(connssl->connecting_state != ssl_connect_2_writing)
-          connssl->connecting_state = ssl_connect_2_reading;
+        connssl->io_need = CURL_SSL_IO_NEED_RECV;
         DEBUGF(infof(data, "schannel: failed to receive handshake, "
                      "need more data"));
         return CURLE_OK;
@@ -1448,7 +1448,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     /* check if the handshake was incomplete */
     if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
       backend->encdata_is_incomplete = true;
-      connssl->connecting_state = ssl_connect_2_reading;
+      connssl->io_need = CURL_SSL_IO_NEED_RECV;
       DEBUGF(infof(data,
                    "schannel: received incomplete message, need more data"));
       return CURLE_OK;
@@ -1460,7 +1460,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
        !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
       backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
-      connssl->connecting_state = ssl_connect_2_writing;
+      connssl->io_need = CURL_SSL_IO_NEED_SEND;
       DEBUGF(infof(data,
                    "schannel: a client certificate has been requested"));
       return CURLE_OK;
@@ -1560,7 +1560,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   /* check if the handshake needs to be continued */
   if(sspi_status == SEC_I_CONTINUE_NEEDED) {
-    connssl->connecting_state = ssl_connect_2_reading;
+    connssl->io_need = CURL_SSL_IO_NEED_RECV;
     return CURLE_OK;
   }
 
@@ -1867,9 +1867,7 @@ schannel_connect_common(struct Curl_cfilter *cf,
       return result;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
+  while(ssl_connect_2 == connssl->connecting_state) {
 
     /* check out how much more time we're allowed */
     timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -1881,13 +1879,12 @@ schannel_connect_common(struct Curl_cfilter *cf,
     }
 
     /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-       || connssl->connecting_state == ssl_connect_2_writing) {
+    if(connssl->io_need) {
 
-      curl_socket_t writefd = ssl_connect_2_writing ==
-        connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading ==
-        connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
+      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+                              sockfd : CURL_SOCKET_BAD;
+      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+                             sockfd : CURL_SOCKET_BAD;
 
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
                                nonblocking ? 0 : timeout_ms);
@@ -1918,10 +1915,7 @@ schannel_connect_common(struct Curl_cfilter *cf,
      * have a valid fdset to wait on.
      */
     result = schannel_connect_step2(cf, data);
-    if(result || (nonblocking &&
-                  (ssl_connect_2 == connssl->connecting_state ||
-                   ssl_connect_2_reading == connssl->connecting_state ||
-                   ssl_connect_2_writing == connssl->connecting_state)))
+    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
       return result;
 
   } /* repeat step2 until all transactions are done. */
@@ -2320,7 +2314,8 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
         /* begin renegotiation */
         infof(data, "schannel: renegotiating SSL/TLS connection");
         connssl->state = ssl_connection_negotiating;
-        connssl->connecting_state = ssl_connect_2_writing;
+        connssl->connecting_state = ssl_connect_2;
+        connssl->io_need = CURL_SSL_IO_NEED_SEND;
         backend->recv_renegotiating = true;
         *err = schannel_connect_common(cf, data, FALSE, &done);
         backend->recv_renegotiating = false;
index 52fdce7cc40e82c02e119fefdf9cf34a07f64e34..a77345a738da70cab3492165419a3bde8313d2d0 100644 (file)
@@ -1948,21 +1948,20 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
   SSLCipherSuite cipher;
   SSLProtocol protocol = 0;
 
-  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
-              || ssl_connect_2_reading == connssl->connecting_state
-              || ssl_connect_2_writing == connssl->connecting_state);
+  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state);
   DEBUGASSERT(backend);
   CURL_TRC_CF(data, cf, "connect_step2");
 
   /* Here goes nothing: */
 check_handshake:
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
   err = SSLHandshake(backend->ssl_ctx);
 
   if(err != noErr) {
     switch(err) {
       case errSSLWouldBlock:  /* they're not done with us yet */
-        connssl->connecting_state = backend->ssl_direction ?
-            ssl_connect_2_writing : ssl_connect_2_reading;
+        connssl->io_need = backend->ssl_direction ?
+            CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
         return CURLE_OK;
 
       /* The below is errSSLServerAuthCompleted; it's not defined in
@@ -2460,9 +2459,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
       return result;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
+  while(ssl_connect_2 == connssl->connecting_state) {
 
     /* check allowed time left */
     const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -2474,13 +2471,12 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
     }
 
     /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading ||
-       connssl->connecting_state == ssl_connect_2_writing) {
+    if(connssl->io_need) {
 
-      curl_socket_t writefd = ssl_connect_2_writing ==
-      connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading ==
-      connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+                              sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+                             sockfd:CURL_SOCKET_BAD;
 
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
                                nonblocking ? 0 : timeout_ms);
@@ -2510,10 +2506,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
      * or epoll() will always have a valid fdset to wait on.
      */
     result = sectransp_connect_step2(cf, data);
-    if(result || (nonblocking &&
-                  (ssl_connect_2 == connssl->connecting_state ||
-                   ssl_connect_2_reading == connssl->connecting_state ||
-                   ssl_connect_2_writing == connssl->connecting_state)))
+    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
       return result;
 
   } /* repeat step2 until all transactions are done. */
index 1a6aa54aa40108be96f44a8ab98a91b8aa049dd5..373c155787de556900ca50db544d066e44d32d7c 100644 (file)
@@ -772,11 +772,12 @@ void Curl_ssl_close_all(struct Curl_easy *data)
 void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
                               struct easy_pollset *ps)
 {
-  if(!cf->connected) {
-    struct ssl_connect_data *connssl = cf->ctx;
+  struct ssl_connect_data *connssl = cf->ctx;
+
+  if(connssl->io_need) {
     curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
     if(sock != CURL_SOCKET_BAD) {
-      if(connssl->connecting_state == ssl_connect_2_writing) {
+      if(connssl->io_need & CURL_SSL_IO_NEED_SEND) {
         Curl_pollset_set_out_only(data, ps, sock);
         CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%"
                     CURL_FORMAT_SOCKET_T, sock);
index 9ac964d114060d927ac33af6b759c3d09cdc85ed..46bc82bbaff01c92f76f93f6f50107c7437bdd29 100644 (file)
@@ -64,15 +64,34 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
                                   const unsigned char *proto,
                                   size_t proto_len);
 
+/* enum for the nonblocking SSL connection state machine */
+typedef enum {
+  ssl_connect_1,
+  ssl_connect_2,
+  ssl_connect_3,
+  ssl_connect_done
+} ssl_connect_state;
+
+typedef enum {
+  ssl_connection_none,
+  ssl_connection_negotiating,
+  ssl_connection_complete
+} ssl_connection_state;
+
+#define CURL_SSL_IO_NEED_NONE   (0)
+#define CURL_SSL_IO_NEED_RECV   (1<<0)
+#define CURL_SSL_IO_NEED_SEND   (1<<1)
+
 /* Information in each SSL cfilter context: cf->ctx */
 struct ssl_connect_data {
-  ssl_connection_state state;
-  ssl_connect_state connecting_state;
   struct ssl_peer peer;
   const struct alpn_spec *alpn;     /* ALPN to use or NULL for none */
   void *backend;                    /* vtls backend specific props */
   struct cf_call_data call_data;    /* data handle used in current call */
   struct curltime handshake_done;   /* time when handshake finished */
+  ssl_connection_state state;
+  ssl_connect_state connecting_state;
+  int io_need;                      /* TLS signals special SEND/RECV needs */
   BIT(use_alpn);                    /* if ALPN shall be used in handshake */
   BIT(peer_closed);                 /* peer has closed connection */
 };
index 7de3c1a10789e2219bb00f41c332b0fee957a086..cbb0fd5785e0c22dbd3fab79b7639b07003ca40b 100644 (file)
@@ -659,11 +659,11 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 #ifndef NO_OLD_TLS
     req_method = TLSv1_1_client_method();
     use_sni(TRUE);
+    break;
 #else
     failf(data, "wolfSSL does not support TLS 1.1");
     return CURLE_NOT_BUILT_IN;
 #endif
-    break;
   case CURL_SSLVERSION_TLSv1_2:
 #ifndef WOLFSSL_NO_TLS12
     req_method = TLSv1_2_client_method();
@@ -1047,6 +1047,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     backend->x509_store_setup = wssl.x509_store_setup;
   }
 
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
   ret = wolfSSL_connect(backend->handle);
 
 #ifdef OPENSSL_EXTRA
@@ -1078,11 +1079,11 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     int  detail = wolfSSL_get_error(backend->handle, ret);
 
     if(SSL_ERROR_WANT_READ == detail) {
-      connssl->connecting_state = ssl_connect_2_reading;
+      connssl->io_need = CURL_SSL_IO_NEED_RECV;
       return CURLE_OK;
     }
     else if(SSL_ERROR_WANT_WRITE == detail) {
-      connssl->connecting_state = ssl_connect_2_writing;
+      connssl->io_need = CURL_SSL_IO_NEED_SEND;
       return CURLE_OK;
     }
     /* There is no easy way to override only the CN matching.
@@ -1524,9 +1525,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
       return result;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
+  while(ssl_connect_2 == connssl->connecting_state) {
 
     /* check allowed time left */
     const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -1538,13 +1537,12 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
     }
 
     /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-       || connssl->connecting_state == ssl_connect_2_writing) {
+    if(connssl->io_need) {
 
-      curl_socket_t writefd = ssl_connect_2_writing ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading ==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+                              sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+                             sockfd:CURL_SOCKET_BAD;
 
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
                                nonblocking?0:timeout_ms);
@@ -1575,10 +1573,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
      * have a valid fdset to wait on.
      */
     result = wolfssl_connect_step2(cf, data);
-    if(result || (nonblocking &&
-                  (ssl_connect_2 == connssl->connecting_state ||
-                   ssl_connect_2_reading == connssl->connecting_state ||
-                   ssl_connect_2_writing == connssl->connecting_state)))
+    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
       return result;
   } /* repeat step2 until all transactions are done. */