vtls: new io_need flags for poll handling

- 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
This commit is contained in:
Stefan Eissing 2024-06-04 14:00:12 +02:00 committed by Daniel Stenberg
parent f75aa2857f
commit 937ba94ed5
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
11 changed files with 114 additions and 135 deletions

View 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;

View 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;
}

View 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,

View 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. */

View 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. */

View 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 */

View 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;

View 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. */

View 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;
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);

View 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 */
};

View 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. */