else {
infof(data, "SSL session allows %zu bytes of early data, "
"reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
- connssl->earlydata_state = ssl_earlydata_use;
+ connssl->earlydata_state = ssl_earlydata_await;
connssl->state = ssl_connection_deferred;
result = Curl_alpn_set_negotiated(cf, data, connssl,
(const unsigned char *)scs->alpn,
return result;
}
-static CURLcode gtls_set_earlydata(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf, size_t blen)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- ssize_t nwritten = 0;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use);
- DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
- if(blen) {
- if(blen > connssl->earlydata_max)
- blen = connssl->earlydata_max;
- nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
- CURL_TRC_CF(data, cf, "gtls_set_earlydata(len=%zu) -> %zd",
- blen, nwritten);
- if(nwritten < 0)
- return result;
- }
- connssl->earlydata_state = ssl_earlydata_sending;
- connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
- return CURLE_OK;
-}
-
static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
}
if(connssl->connecting_state == ssl_connect_2) {
- if(connssl->earlydata_state == ssl_earlydata_use) {
+ if(connssl->earlydata_state == ssl_earlydata_await) {
goto out;
}
else if(connssl->earlydata_state == ssl_earlydata_sending) {
if(result)
goto out;
connssl->earlydata_state = ssl_earlydata_sent;
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
}
DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
(connssl->earlydata_state == ssl_earlydata_sent));
if(result)
goto out;
- if(connssl->earlydata_state == ssl_earlydata_sent) {
- /* report the true time the handshake was done */
- connssl->handshake_done = Curl_now();
- Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
- if(gnutls_session_get_flags(backend->gtls.session) &
- GNUTLS_SFLAGS_EARLY_DATA) {
- connssl->earlydata_state = ssl_earlydata_accepted;
- infof(data, "Server accepted %zu bytes of TLS early data.",
- connssl->earlydata_skip);
- }
- else {
- connssl->earlydata_state = ssl_earlydata_rejected;
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
- infof(data, "Server rejected TLS early data.");
- connssl->earlydata_skip = 0;
- }
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (gnutls_session_get_flags(backend->gtls.session) &
+ GNUTLS_SFLAGS_EARLY_DATA) ?
+ ssl_earlydata_accepted : ssl_earlydata_rejected;
}
connssl->connecting_state = ssl_connect_done;
}
- if(ssl_connect_done == connssl->connecting_state) {
- connssl->state = ssl_connection_complete;
- *done = TRUE;
- }
+ if(connssl->connecting_state == ssl_connect_done)
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
out:
if(result == CURLE_AGAIN) {
bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
- if(connssl->state == ssl_connection_deferred) {
+ if((connssl->state == ssl_connection_deferred) &&
+ (connssl->earlydata_state == ssl_earlydata_await)) {
/* We refuse to be pushed, we are waiting for someone to send/recv. */
*done = TRUE;
return CURLE_OK;
return gtls_connect_common(cf, data, done);
}
-static CURLcode gtls_connect_deferred(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf,
- size_t blen,
- bool *done)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->state == ssl_connection_deferred);
- *done = FALSE;
- if(connssl->earlydata_state == ssl_earlydata_use) {
- result = gtls_set_earlydata(cf, data, buf, blen);
- if(result)
- return result;
- }
-
- return gtls_connect_common(cf, data, done);
-}
-
static bool gtls_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
ssize_t rc;
size_t nwritten, total_written = 0;
+ (void)data;
DEBUGASSERT(backend);
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = gtls_connect_deferred(cf, data, buf, blen, &done);
- if(*curlcode) {
- rc = -1;
- goto out;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- rc = -1;
- goto out;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
- if(connssl->earlydata_skip) {
- if(connssl->earlydata_skip >= blen) {
- connssl->earlydata_skip -= blen;
- *curlcode = CURLE_OK;
- rc = (ssize_t)blen;
- goto out;
- }
- else {
- total_written += connssl->earlydata_skip;
- buf = ((const char *)buf) + connssl->earlydata_skip;
- blen -= connssl->earlydata_skip;
- connssl->earlydata_skip = 0;
- }
- }
-
while(blen) {
backend->gtls.io_result = CURLE_OK;
rc = gnutls_record_send(backend->gtls.session, buf, blen);
(void)data;
DEBUGASSERT(backend);
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = gtls_connect_deferred(cf, data, NULL, 0, &done);
- if(*curlcode) {
- ret = -1;
- goto out;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- ret = -1;
- goto out;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
*curlcode = CURLE_AGAIN;
}
}
-static CURLcode
-ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done)
-{
- struct ssl_connect_data *connssl = cf->ctx;
-
- if(!ssl_prefs_check(data))
- return CURLE_SSL_CONNECT_ERROR;
-
- /* mark this is being ssl requested from here on. */
- return connssl->ssl_impl->do_connect(cf, data, done);
-}
-
CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
struct dynbuf *binding)
{
struct cf_call_data save;
CURLcode result;
- if(cf->connected) {
+ if(cf->connected && (connssl->state != ssl_connection_deferred)) {
*done = TRUE;
return CURLE_OK;
}
CF_DATA_SAVE(save, cf, data);
CURL_TRC_CF(data, cf, "cf_connect()");
- DEBUGASSERT(data->conn);
- DEBUGASSERT(data->conn == cf->conn);
DEBUGASSERT(connssl);
*done = FALSE;
goto out;
}
- result = ssl_connect(cf, data, done);
+ if(!connssl->prefs_checked) {
+ if(!ssl_prefs_check(data))
+ return CURLE_SSL_CONNECT_ERROR;
+ connssl->prefs_checked = TRUE;
+ }
+
+ result = connssl->ssl_impl->do_connect(cf, data, done);
if(!result && *done) {
cf->connected = TRUE;
/* Connection can be deferred when sending early data */
DEBUGASSERT(connssl->state == ssl_connection_complete ||
connssl->state == ssl_connection_deferred);
+ DEBUGASSERT(connssl->state != ssl_connection_deferred ||
+ connssl->earlydata_state > ssl_earlydata_none);
}
out:
CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done);
return result;
}
+static CURLcode ssl_cf_set_earlydata(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ ssize_t nwritten = 0;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_await);
+ DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
+ if(blen) {
+ if(blen > connssl->earlydata_max)
+ blen = connssl->earlydata_max;
+ nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
+ CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd",
+ blen, nwritten);
+ if(nwritten < 0)
+ return result;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssl_cf_connect_deferred(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
+ bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(connssl->state == ssl_connection_deferred);
+ *done = FALSE;
+ if(connssl->earlydata_state == ssl_earlydata_await) {
+ result = ssl_cf_set_earlydata(cf, data, buf, blen);
+ if(result)
+ return result;
+ /* we buffered any early data we'd like to send. Actually
+ * do the connect now which sends it and performs the handshake. */
+ connssl->earlydata_state = ssl_earlydata_sending;
+ connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
+ }
+
+ result = ssl_cf_connect(cf, data, done);
+
+ if(!result && *done) {
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
+ switch(connssl->earlydata_state) {
+ case ssl_earlydata_none:
+ break;
+ case ssl_earlydata_accepted:
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
+ infof(data, "Server accepted %zu bytes of TLS early data.",
+ connssl->earlydata_skip);
+ break;
+ case ssl_earlydata_rejected:
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
+ infof(data, "Server rejected TLS early data.");
+ connssl->earlydata_skip = 0;
+ break;
+ default:
+ /* This should not happen. Either we do not use early data or we
+ * should know if it was accepted or not. */
+ DEBUGASSERT(NULL);
+ break;
+ }
+ }
+ return result;
+}
+
static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
}
static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
- struct Curl_easy *data, const void *buf, size_t len,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
bool eos, CURLcode *err)
{
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
- ssize_t nwritten = 0;
+ ssize_t nwritten = 0, early_written = 0;
(void)eos;
- /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
*err = CURLE_OK;
- if(len > 0) {
- CF_DATA_SAVE(save, cf, data);
- nwritten = connssl->ssl_impl->send_plain(cf, data, buf, len, err);
- CF_DATA_RESTORE(cf, save);
+ CF_DATA_SAVE(save, cf, data);
+
+ if(connssl->state == ssl_connection_deferred) {
+ bool done = FALSE;
+ *err = ssl_cf_connect_deferred(cf, data, buf, blen, &done);
+ if(*err) {
+ nwritten = -1;
+ goto out;
+ }
+ else if(!done) {
+ *err = CURLE_AGAIN;
+ nwritten = -1;
+ goto out;
+ }
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
}
+
+ if(connssl->earlydata_skip) {
+ if(connssl->earlydata_skip >= blen) {
+ connssl->earlydata_skip -= blen;
+ *err = CURLE_OK;
+ nwritten = (ssize_t)blen;
+ goto out;
+ }
+ else {
+ early_written = connssl->earlydata_skip;
+ buf = ((const char *)buf) + connssl->earlydata_skip;
+ blen -= connssl->earlydata_skip;
+ connssl->earlydata_skip = 0;
+ }
+ }
+
+ /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
+ if(blen > 0)
+ nwritten = connssl->ssl_impl->send_plain(cf, data, buf, blen, err);
+
+ if(nwritten >= 0)
+ nwritten += early_written;
+
+out:
+ CF_DATA_RESTORE(cf, save);
return nwritten;
}
CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
+ if(connssl->state == ssl_connection_deferred) {
+ bool done = FALSE;
+ *err = ssl_cf_connect_deferred(cf, data, NULL, 0, &done);
+ if(*err) {
+ nread = -1;
+ goto out;
+ }
+ else if(!done) {
+ *err = CURLE_AGAIN;
+ nread = -1;
+ goto out;
+ }
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ }
+
nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err);
if(nread > 0) {
DEBUGASSERT((size_t)nread <= len);
/* eof */
*err = CURLE_OK;
}
+
+out:
CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
nread, *err);
CF_DATA_RESTORE(cf, save);
CURLcode result = CURLE_OK;
*done = TRUE;
- if(!cf->shutdown && Curl_ssl->shut_down) {
+ /* If we have done the SSL handshake, shut down the connection cleanly */
+ if(cf->connected && (connssl->state == ssl_connection_complete) &&
+ !cf->shutdown && Curl_ssl->shut_down) {
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
typedef enum {
ssl_earlydata_none,
- ssl_earlydata_use,
+ ssl_earlydata_await,
ssl_earlydata_sending,
ssl_earlydata_sent,
ssl_earlydata_accepted,
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 */
+ BIT(prefs_checked); /* SSL preferences have been checked */
};
else {
infof(data, "SSL session allows %zu bytes of early data, "
"reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
- connssl->earlydata_state = ssl_earlydata_use;
+ connssl->earlydata_state = ssl_earlydata_await;
connssl->state = ssl_connection_deferred;
result = Curl_alpn_set_negotiated(cf, data, connssl,
(const unsigned char *)scs->alpn,
}
DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
(connssl->earlydata_state == ssl_earlydata_sent));
+#else
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_none);
#endif /* WOLFSSL_EARLY_DATA */
wolfSSL_ERR_clear_error();
}
}
-#ifdef WOLFSSL_EARLY_DATA
-static CURLcode wssl_set_earlydata(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf, size_t blen)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- ssize_t nwritten = 0;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use);
- DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
- if(blen) {
- if(blen > connssl->earlydata_max)
- blen = connssl->earlydata_max;
- nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
- CURL_TRC_CF(data, cf, "wssl_set_earlydata(len=%zu) -> %zd",
- blen, nwritten);
- if(nwritten < 0)
- return result;
- }
- connssl->earlydata_state = ssl_earlydata_sending;
- connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
- return CURLE_OK;
-}
-
-static CURLcode wssl_connect_deferred(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf,
- size_t blen,
- bool *done)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->state == ssl_connection_deferred);
- *done = FALSE;
- if(connssl->earlydata_state == ssl_earlydata_use) {
- result = wssl_set_earlydata(cf, data, buf, blen);
- if(result)
- return result;
- }
-
- return wssl_connect(cf, data, done);
-}
-#endif /* WOLFSSL_EARLY_DATA */
-
static ssize_t wssl_send(struct Curl_cfilter *cf,
struct Curl_easy *data,
const void *buf, size_t blen,
wolfSSL_ERR_clear_error();
-#ifdef WOLFSSL_EARLY_DATA
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = wssl_connect_deferred(cf, data, buf, blen, &done);
- if(*curlcode) {
- nwritten = -1;
- goto out;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- nwritten = -1;
- goto out;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
- if(connssl->earlydata_skip) {
- if(connssl->earlydata_skip >= blen) {
- connssl->earlydata_skip -= blen;
- *curlcode = CURLE_OK;
- nwritten = (ssize_t)blen;
- goto out;
- }
- else {
- total_written += connssl->earlydata_skip;
- buf = ((const char *)buf) + connssl->earlydata_skip;
- blen -= connssl->earlydata_skip;
- connssl->earlydata_skip = 0;
- }
- }
-#endif /* WOLFSSL_EARLY_DATA */
-
if(blen) {
int memlen = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
int rc;
DEBUGASSERT(wssl);
-#ifdef WOLFSSL_EARLY_DATA
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = wssl_connect_deferred(cf, data, NULL, 0, &done);
- if(*curlcode) {
- return -1;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- return-1;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-#endif
-
wolfSSL_ERR_clear_error();
*curlcode = CURLE_OK;
}
if(ssl_connect_2 == connssl->connecting_state) {
- if(connssl->earlydata_state == ssl_earlydata_use) {
+ if(connssl->earlydata_state == ssl_earlydata_await) {
/* We defer the handshake until request data arrives. */
DEBUGASSERT(connssl->state == ssl_connection_deferred);
goto out;
connssl->state = ssl_connection_complete;
#ifdef WOLFSSL_EARLY_DATA
- if(connssl->earlydata_state == ssl_earlydata_sent) {
- /* report the true time the handshake was done */
- connssl->handshake_done = Curl_now();
- Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
- if(wolfSSL_get_early_data_status(wssl->ssl) ==
- WOLFSSL_EARLY_DATA_REJECTED) {
- connssl->earlydata_state = ssl_earlydata_rejected;
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
- infof(data, "Server rejected TLS early data.");
- connssl->earlydata_skip = 0;
- }
- else if(connssl->earlydata_skip) {
- connssl->earlydata_state = ssl_earlydata_accepted;
- infof(data, "Server accepted %zu bytes of TLS early data.",
- connssl->earlydata_skip);
- }
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (wolfSSL_get_early_data_status(wssl->ssl) ==
+ WOLFSSL_EARLY_DATA_REJECTED) ?
+ ssl_earlydata_rejected : ssl_earlydata_accepted;
}
#endif /* WOLFSSL_EARLY_DATA */
}