summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/vquic
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/vquic')
-rw-r--r--libs/libcurl/src/vquic/ngtcp2.c223
-rw-r--r--libs/libcurl/src/vquic/ngtcp2.h8
2 files changed, 96 insertions, 135 deletions
diff --git a/libs/libcurl/src/vquic/ngtcp2.c b/libs/libcurl/src/vquic/ngtcp2.c
index fc449c4152..dfe8f96fb1 100644
--- a/libs/libcurl/src/vquic/ngtcp2.c
+++ b/libs/libcurl/src/vquic/ngtcp2.c
@@ -28,6 +28,9 @@
#include <nghttp3/nghttp3.h>
#ifdef USE_OPENSSL
#include <openssl/err.h>
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#elif defined(USE_GNUTLS)
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
#endif
#include "urldata.h"
#include "sendf.h"
@@ -58,6 +61,7 @@
#endif
#define H3_ALPN_H3_29 "\x5h3-29"
+#define H3_ALPN_H3 "\x2h3"
/*
* This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
@@ -117,42 +121,6 @@ static void quic_printf(void *user_data, const char *fmt, ...)
}
#endif
-#ifdef USE_OPENSSL
-static ngtcp2_crypto_level
-quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level)
-{
- switch(ossl_level) {
- case ssl_encryption_initial:
- return NGTCP2_CRYPTO_LEVEL_INITIAL;
- case ssl_encryption_early_data:
- return NGTCP2_CRYPTO_LEVEL_EARLY;
- case ssl_encryption_handshake:
- return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
- case ssl_encryption_application:
- return NGTCP2_CRYPTO_LEVEL_APPLICATION;
- default:
- assert(0);
- }
-}
-#elif defined(USE_GNUTLS)
-static ngtcp2_crypto_level
-quic_from_gtls_level(gnutls_record_encryption_level_t gtls_level)
-{
- switch(gtls_level) {
- case GNUTLS_ENCRYPTION_LEVEL_INITIAL:
- return NGTCP2_CRYPTO_LEVEL_INITIAL;
- case GNUTLS_ENCRYPTION_LEVEL_EARLY:
- return NGTCP2_CRYPTO_LEVEL_EARLY;
- case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE:
- return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
- case GNUTLS_ENCRYPTION_LEVEL_APPLICATION:
- return NGTCP2_CRYPTO_LEVEL_APPLICATION;
- default:
- assert(0);
- }
-}
-#endif
-
static void qlog_callback(void *user_data, uint32_t flags,
const void *data, size_t datalen)
{
@@ -223,27 +191,9 @@ static int write_client_handshake(struct quicsocket *qs,
ngtcp2_crypto_level level,
const uint8_t *data, size_t len)
{
- struct quic_handshake *crypto_data;
int rv;
- crypto_data = &qs->crypto_data[level];
- if(!crypto_data->buf) {
- crypto_data->buf = malloc(4096);
- if(!crypto_data->buf)
- return 0;
- crypto_data->alloclen = 4096;
- }
-
- /* TODO Just pretend that handshake does not grow more than 4KiB for
- now */
- assert(crypto_data->len + len <= crypto_data->alloclen);
-
- memcpy(&crypto_data->buf[crypto_data->len], data, len);
- crypto_data->len += len;
-
- rv = ngtcp2_conn_submit_crypto_data(
- qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len),
- len);
+ rv = ngtcp2_conn_submit_crypto_data(qs->qconn, level, data, len);
if(rv) {
H3BUGF(fprintf(stderr, "write_client_handshake failed\n"));
}
@@ -260,7 +210,7 @@ static int quic_set_encryption_secrets(SSL *ssl,
size_t secretlen)
{
struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
- int level = quic_from_ossl_level(ossl_level);
+ int level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
if(ngtcp2_crypto_derive_and_install_rx_key(
qs->qconn, NULL, NULL, NULL, level, rx_secret, secretlen) != 0)
@@ -282,7 +232,8 @@ static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *data, size_t len)
{
struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
- ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
return write_client_handshake(qs, level, data, len);
}
@@ -353,9 +304,10 @@ static int quic_init_ssl(struct quicsocket *qs)
SSL_set_app_data(qs->ssl, qs);
SSL_set_connect_state(qs->ssl);
+ SSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
- alpn = (const uint8_t *)H3_ALPN_H3_29;
- alpnlen = sizeof(H3_ALPN_H3_29) - 1;
+ alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+ alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
if(alpn)
SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
@@ -370,7 +322,8 @@ static int secret_func(gnutls_session_t ssl,
const void *tx_secret, size_t secretlen)
{
struct quicsocket *qs = gnutls_session_get_ptr(ssl);
- int level = quic_from_gtls_level(gtls_level);
+ int level =
+ ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
if(level != NGTCP2_CRYPTO_LEVEL_EARLY &&
ngtcp2_crypto_derive_and_install_rx_key(
@@ -395,7 +348,8 @@ static int read_func(gnutls_session_t ssl,
size_t len)
{
struct quicsocket *qs = gnutls_session_get_ptr(ssl);
- ngtcp2_crypto_level level = quic_from_gtls_level(gtls_level);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
int rv;
if(htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
@@ -465,7 +419,7 @@ static int tp_send_func(gnutls_session_t ssl, gnutls_buffer_t extdata)
static int quic_init_ssl(struct quicsocket *qs)
{
- gnutls_datum_t alpn = {NULL, 0};
+ gnutls_datum_t alpn[2];
/* this will need some attention when HTTPS proxy over QUIC get fixed */
const char * const hostname = qs->conn->host.name;
int rc;
@@ -487,12 +441,10 @@ static int quic_init_ssl(struct quicsocket *qs)
gnutls_alert_set_read_function(qs->ssl, alert_read_func);
rc = gnutls_session_ext_register(qs->ssl, "QUIC Transport Parameters",
- 0xffa5, GNUTLS_EXT_TLS,
- tp_recv_func, tp_send_func,
- NULL, NULL, NULL,
- GNUTLS_EXT_FLAG_TLS |
- GNUTLS_EXT_FLAG_CLIENT_HELLO |
- GNUTLS_EXT_FLAG_EE);
+ NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1, GNUTLS_EXT_TLS,
+ tp_recv_func, tp_send_func, NULL, NULL, NULL,
+ GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
+ GNUTLS_EXT_FLAG_EE);
if(rc < 0) {
H3BUGF(fprintf(stderr, "gnutls_session_ext_register failed: %s\n",
gnutls_strerror(rc)));
@@ -532,10 +484,12 @@ static int quic_init_ssl(struct quicsocket *qs)
}
/* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
- alpn.data = (unsigned char *)H3_ALPN_H3_29 + 1;
- alpn.size = sizeof(H3_ALPN_H3_29) - 2;
- if(alpn.data)
- gnutls_alpn_set_protocols(qs->ssl, &alpn, 1, 0);
+ alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
+ alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
+ alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
+ alpn[1].size = sizeof(H3_ALPN_H3) - 2;
+
+ gnutls_alpn_set_protocols(qs->ssl, alpn, 2, GNUTLS_ALPN_MANDATORY);
/* set SNI */
gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname));
@@ -543,22 +497,6 @@ static int quic_init_ssl(struct quicsocket *qs)
}
#endif
-static int
-cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level,
- uint64_t offset,
- const uint8_t *data, size_t datalen,
- void *user_data)
-{
- (void)offset;
- (void)user_data;
-
- if(ngtcp2_crypto_read_write_crypto_data(tconn, crypto_level, data,
- datalen) != 0)
- return NGTCP2_ERR_CRYPTO;
-
- return 0;
-}
-
static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
{
(void)user_data;
@@ -623,8 +561,8 @@ cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
return 0;
}
-static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t app_error_code,
+static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
+ int64_t stream_id, uint64_t app_error_code,
void *user_data, void *stream_user_data)
{
struct quicsocket *qs = (struct quicsocket *)user_data;
@@ -633,6 +571,10 @@ static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
(void)stream_user_data;
/* stream is closed... */
+ if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
+ app_error_code = NGHTTP3_H3_NO_ERROR;
+ }
+
rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
app_error_code);
if(rv) {
@@ -653,7 +595,25 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
(void)app_error_code;
(void)stream_user_data;
- rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id);
+ rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)app_error_code;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
if(rv) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -690,6 +650,20 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
return 0;
}
+static void cb_rand(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx)
+{
+ CURLcode result;
+ (void)rand_ctx;
+
+ result = Curl_rand(NULL, dest, destlen);
+ if(result) {
+ /* cb_rand is only used for non-cryptographic context. If Curl_rand
+ failed, just fill 0 and call it *random*. */
+ memset(dest, 0, destlen);
+ }
+}
+
static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
uint8_t *token, size_t cidlen,
void *user_data)
@@ -713,14 +687,13 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
static ngtcp2_callbacks ng_callbacks = {
ngtcp2_crypto_client_initial_cb,
NULL, /* recv_client_initial */
- cb_recv_crypto_data,
+ ngtcp2_crypto_recv_crypto_data_cb,
cb_handshake_completed,
NULL, /* recv_version_negotiation */
ngtcp2_crypto_encrypt_cb,
ngtcp2_crypto_decrypt_cb,
ngtcp2_crypto_hp_mask_cb,
cb_recv_stream_data,
- NULL, /* acked_crypto_offset */
cb_acked_stream_data_offset,
NULL, /* stream_open */
cb_stream_close,
@@ -728,7 +701,7 @@ static ngtcp2_callbacks ng_callbacks = {
ngtcp2_crypto_recv_retry_cb,
cb_extend_max_local_streams_bidi,
NULL, /* extend_max_local_streams_uni */
- NULL, /* rand */
+ cb_rand,
cb_get_new_connection_id,
NULL, /* remove_connection_id */
ngtcp2_crypto_update_key_cb, /* update_key */
@@ -745,7 +718,9 @@ static ngtcp2_callbacks ng_callbacks = {
ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
NULL, /* recv_datagram */
NULL, /* ack_datagram */
- NULL /* lost_datagram */
+ NULL, /* lost_datagram */
+ ngtcp2_crypto_get_path_challenge_data_cb,
+ cb_stream_stop_sending
};
/*
@@ -817,7 +792,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
ngtcp2_addr_init(&path.remote, addr, addrlen);
rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path,
- NGTCP2_PROTO_VER_MIN, &ng_callbacks,
+ NGTCP2_PROTO_VER_V1, &ng_callbacks,
&qs->settings, &qs->transport_params, NULL, qs);
if(rc)
return CURLE_QUIC_CONNECT_ERROR;
@@ -833,7 +808,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
void Curl_quic_ver(char *p, size_t len)
{
const ngtcp2_info *ng2 = ngtcp2_version(0);
- nghttp3_info *ht3 = nghttp3_version(0);
+ const nghttp3_info *ht3 = nghttp3_version(0);
(void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
ng2->version_str, ht3->version_str);
}
@@ -859,7 +834,6 @@ static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
static void qs_disconnect(struct quicsocket *qs)
{
- int i;
if(!qs->conn) /* already closed */
return;
qs->conn = NULL;
@@ -880,8 +854,6 @@ static void qs_disconnect(struct quicsocket *qs)
qs->cred = NULL;
}
#endif
- for(i = 0; i < 3; i++)
- Curl_safefree(qs->crypto_data[i].buf);
nghttp3_conn_del(qs->h3conn);
ngtcp2_conn_del(qs->qconn);
#ifdef USE_OPENSSL
@@ -1143,14 +1115,10 @@ static nghttp3_callbacks ngh3_callbacks = {
NULL, /* begin_trailers */
cb_h3_recv_header,
NULL, /* end_trailers */
- NULL, /* http_begin_push_promise */
- NULL, /* http_recv_push_promise */
- NULL, /* http_end_push_promise */
- NULL, /* http_cancel_push */
cb_h3_send_stop_sending,
- NULL, /* push_stream */
NULL, /* end_stream */
NULL, /* reset_stream */
+ NULL /* shutdown */
};
static int init_ngh3_conn(struct quicsocket *qs)
@@ -1660,6 +1628,12 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
return -1;
}
+ /* Reset post upload buffer after resumed. */
+ if(stream->upload_mem) {
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+ }
+
*curlcode = CURLE_OK;
return sent;
}
@@ -1758,8 +1732,7 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
int rv;
ssize_t sent;
ssize_t outlen;
- uint8_t out[NGTCP2_MAX_PKTLEN_IPV4];
- size_t pktlen;
+ uint8_t out[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
ngtcp2_path_storage ps;
ngtcp2_tstamp ts = timestamp();
struct sockaddr_storage remote_addr;
@@ -1772,19 +1745,6 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
ssize_t ndatalen;
uint32_t flags;
- switch(qs->local_addr.ss_family) {
- case AF_INET:
- pktlen = NGTCP2_MAX_PKTLEN_IPV4;
- break;
-#ifdef ENABLE_IPV6
- case AF_INET6:
- pktlen = NGTCP2_MAX_PKTLEN_IPV6;
- break;
-#endif
- default:
- assert(0);
- }
-
rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
if(rv) {
failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
@@ -1811,15 +1771,16 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
(fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
- outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, out, pktlen,
+ outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, out,
+ sizeof(out),
&ndatalen, flags, stream_id,
(const ngtcp2_vec *)vec, veccnt, ts);
if(outlen == 0) {
break;
}
if(outlen < 0) {
- if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED ||
- outlen == NGTCP2_ERR_STREAM_SHUT_WR) {
+ switch(outlen) {
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
assert(ndatalen == -1);
rv = nghttp3_conn_block_stream(qs->h3conn, stream_id);
if(rv) {
@@ -1828,8 +1789,17 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
return CURLE_SEND_ERROR;
}
continue;
- }
- else if(outlen == NGTCP2_ERR_WRITE_MORE) {
+ case NGTCP2_ERR_STREAM_SHUT_WR:
+ assert(ndatalen == -1);
+ rv = nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
+ if(rv) {
+ failf(data,
+ "nghttp3_conn_shutdown_stream_write returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ continue;
+ case NGTCP2_ERR_WRITE_MORE:
assert(ndatalen >= 0);
rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
if(rv) {
@@ -1838,8 +1808,7 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
return CURLE_SEND_ERROR;
}
continue;
- }
- else {
+ default:
assert(ndatalen == -1);
failf(data, "ngtcp2_conn_writev_stream returned error: %s",
ngtcp2_strerror((int)outlen));
diff --git a/libs/libcurl/src/vquic/ngtcp2.h b/libs/libcurl/src/vquic/ngtcp2.h
index cbede45146..501453042b 100644
--- a/libs/libcurl/src/vquic/ngtcp2.h
+++ b/libs/libcurl/src/vquic/ngtcp2.h
@@ -34,13 +34,6 @@
#include <gnutls/gnutls.h>
#endif
-struct quic_handshake {
- char *buf; /* pointer to the buffer */
- size_t alloclen; /* size of allocation */
- size_t len; /* size of content in buffer */
- size_t nread; /* how many bytes have been read */
-};
-
struct quicsocket {
struct connectdata *conn; /* point back to the connection */
ngtcp2_conn *qconn;
@@ -56,7 +49,6 @@ struct quicsocket {
gnutls_certificate_credentials_t cred;
gnutls_session_t ssl;
#endif
- struct quic_handshake crypto_data[3];
/* the last TLS alert description generated by the local endpoint */
uint8_t tls_alert;
struct sockaddr_storage local_addr;