diff --git a/configure.py b/configure.py index 79d2e6709c98f1..c60dc30188af96 100755 --- a/configure.py +++ b/configure.py @@ -1205,6 +1205,65 @@ def get_gas_version(cc): warn(f'Could not recognize `gas`: {gas_ret}') return '0.0' +def get_openssl_version(): + """Parse OpenSSL version from opensslv.h header file. + + Returns the version as a number matching OPENSSL_VERSION_NUMBER format: + 0xMNN00PPSL where M=major, NN=minor, PP=patch, S=status(0xf=release,0x0=pre), L=0 + """ + + try: + # Use the C compiler to extract preprocessor macros from opensslv.h + args = ['-E', '-dM', '-include', 'openssl/opensslv.h', '-'] + if not options.shared_openssl: + args = ['-I', 'deps/openssl/openssl/include'] + args + elif options.shared_openssl_includes: + args = ['-I', options.shared_openssl_includes] + args + + proc = subprocess.Popen( + shlex.split(CC) + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + with proc: + proc.stdin.write(b'\n') + out = to_utf8(proc.communicate()[0]) + + if proc.returncode != 0: + warn('Failed to extract OpenSSL version from opensslv.h header') + return 0 + + # Parse the macro definitions + macros = {} + for line in out.split('\n'): + if line.startswith('#define OPENSSL_VERSION_'): + parts = line.split() + if len(parts) >= 3: + macro_name = parts[1] + macro_value = parts[2] + macros[macro_name] = macro_value + + # Extract version components + major = int(macros.get('OPENSSL_VERSION_MAJOR', '0')) + minor = int(macros.get('OPENSSL_VERSION_MINOR', '0')) + patch = int(macros.get('OPENSSL_VERSION_PATCH', '0')) + + # Check if it's a pre-release (has non-empty PRE_RELEASE string) + pre_release = macros.get('OPENSSL_VERSION_PRE_RELEASE', '""').strip('"') + status = 0x0 if pre_release else 0xf + # Construct version number: 0xMNN00PPSL + version_number = ((major << 28) | + (minor << 20) | + (patch << 4) | + status) + + return version_number + + except (OSError, ValueError, subprocess.SubprocessError) as e: + warn(f'Failed to determine OpenSSL version from header: {e}') + return 0 + # Note: Apple clang self-reports as clang 4.2.0 and gcc 4.2.1. It passes # the version check more by accident than anything else but a more rigorous # check involves checking the build number against an allowlist. I'm not @@ -1828,6 +1887,8 @@ def without_ssl_error(option): if options.quic: o['defines'] += ['NODE_OPENSSL_HAS_QUIC'] + o['variables']['openssl_version'] = get_openssl_version() + configure_library('openssl', o) def configure_sqlite(o): diff --git a/deps/ngtcp2/README.md b/deps/ngtcp2/README.md index 3585fa1f3490c7..f3bf7e03957c92 100644 --- a/deps/ngtcp2/README.md +++ b/deps/ngtcp2/README.md @@ -36,6 +36,9 @@ $ cp -R lib/* ../node/deps/ngtcp2/ngtcp2/lib/ $ cp -R crypto/* ../node/deps/ngtcp2/ngtcp2/crypto/ ``` +Be sure to also update the `ngtcp2.gyp` file to reflect any changes in +the source files or include directories. + ### Updating nghttp3 To update nghttp3, replace `v0.7.0` with the desired git tag: @@ -47,3 +50,6 @@ $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only $ cp -R lib/* ../node/deps/ngtcp2/nghttp3/lib/ ``` + +Be sure to also update the `ngtcp2.gyp` file to reflect any changes in +the source files or include directories. diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h index 6cf947487bf899..83820fc983bd84 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -1116,11 +1116,43 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; * * :macro:`NGHTTP3_ERR_NOMEM` * Out of memory. + * + * See also `nghttp3_qpack_encoder_new2`. This function calls + * `nghttp3_qpack_encoder_new2` with the given parameters and 0 as + * seed. */ NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, size_t hard_max_dtable_capacity, const nghttp3_mem *mem); +/** + * @function + * + * `nghttp3_qpack_encoder_new2` initializes QPACK encoder. |pencoder| + * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity. |seed| must be unpredictable + * value, and is used to seed the internal data structure. |mem| is a + * memory allocator. This function allocates memory for + * :type:`nghttp3_qpack_encoder` itself, and assigns its pointer to + * |*pencoder| if it succeeds. + * + * The maximum dynamic table capacity is still 0. In order to change + * the maximum dynamic table capacity, call + * `nghttp3_qpack_encoder_set_max_dtable_capacity`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * + * This function is available since v1.11.0. + */ +NGHTTP3_EXTERN int nghttp3_qpack_encoder_new2(nghttp3_qpack_encoder **pencoder, + size_t hard_max_dtable_capacity, + uint64_t seed, + const nghttp3_mem *mem); + /** * @function * @@ -1605,7 +1637,8 @@ NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback( typedef struct nghttp3_conn nghttp3_conn; #define NGHTTP3_SETTINGS_V1 1 -#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V1 +#define NGHTTP3_SETTINGS_V2 2 +#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V2 /** * @struct @@ -1652,6 +1685,21 @@ typedef struct nghttp3_settings { * Datagrams (see :rfc:`9297`). */ uint8_t h3_datagram; + /* The following fields have been added since NGHTTP3_SETTINGS_V2. */ + /** + * :member:`origin_list`, if set, must contain a serialized HTTP/3 + * ORIGIN frame (see :rfc:`9412`) payload. The ORIGIN frame payload + * is a sequence of zero or more of a length prefixed byte string. + * The length is encoded in 2 bytes in network byte order. If + * :member:`origin_list->len ` is zero, an empty + * ORIGIN frame is sent. An application must keep the buffer + * pointed by :member:`origin_list->base ` alive + * until the :type:`nghttp3_conn` to which this field was passed is + * freed by `nghttp3_conn_del`. The object pointed to by this field + * is copied internally, and does not need to be kept alive. Only + * server uses this field. This field is available since v1.11.0. + */ + const nghttp3_vec *origin_list; } nghttp3_settings; /** @@ -1891,8 +1939,47 @@ typedef int (*nghttp3_recv_settings)(nghttp3_conn *conn, const nghttp3_settings *settings, void *conn_user_data); +/** + * @functypedef + * + * :type:`nghttp3_recv_origin` is a callback function which is invoked + * when a single origin in ORIGIN frame is received. |origin| is a + * received origin of length |originlen|. |originlen| never be 0. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_origin)(nghttp3_conn *conn, const uint8_t *origin, + size_t originlen, void *conn_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_end_origin` is a callback function which is invoked + * when an ORIGIN frame has been completely processed. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_origin)(nghttp3_conn *conn, void *conn_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_rand` is a callback function which is invoked when + * unpredictable data of |destlen| bytes are needed. The + * implementation must write unpredictable data of |destlen| bytes + * into the buffer pointed by |dest|. + */ +typedef void (*nghttp3_rand)(uint8_t *dest, size_t destlen); + #define NGHTTP3_CALLBACKS_V1 1 -#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_V1 +#define NGHTTP3_CALLBACKS_V2 2 +#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_V2 /** * @struct @@ -1986,6 +2073,28 @@ typedef struct nghttp3_callbacks { * when SETTINGS frame is received. */ nghttp3_recv_settings recv_settings; + /* The following fields have been added since NGHTTP3_CALLBACKS_V2. */ + /** + * :member:`recv_origin` is a callback function which is invoked + * when a single origin in an ORIGIN frame is received. This field + * is available since v1.11.0. + */ + nghttp3_recv_origin recv_origin; + /** + * :member:`end_origin` is a callback function which is invoked when + * an ORIGIN frame has been completely processed. This field is + * available since v1.11.0. + */ + nghttp3_end_origin end_origin; + /** + * :member:`rand` is a callback function which is invoked when + * unpredictable data are needed. Although this field is optional + * due to the backward compatibility, it is recommended to specify + * this field to harden the runtime behavior against suspicious + * activities of a remote endpoint. This field is available since + * v1.11.0. + */ + nghttp3_rand rand; } nghttp3_callbacks; /** @@ -2106,7 +2215,7 @@ NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, * control credit (both stream and connection) of underlying QUIC * connection by that amount. It does not include the amount of data * carried by DATA frame which contains application data (excluding - * any control or QPACK unidirectional streams) . See + * any control or QPACK unidirectional streams). See * :type:`nghttp3_recv_data` to handle those bytes. If |fin| is * nonzero, this is the last data from remote endpoint in this stream. * @@ -2480,8 +2589,6 @@ typedef struct nghttp3_data_reader { * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` - * |stream_id| identifies unidirectional stream. * :macro:`NGHTTP3_ERR_CONN_CLOSING` * Connection is shutting down, and no new stream is allowed. * :macro:`NGHTTP3_ERR_STREAM_IN_USE` diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h index 7f6cb8acffe672..bd442b8be636ee 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h @@ -31,7 +31,7 @@ * * Version number of the nghttp3 library release. */ -#define NGHTTP3_VERSION "1.6.0" +#define NGHTTP3_VERSION "1.11.0" /** * @macro @@ -41,6 +41,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGHTTP3_VERSION_NUM 0x010600 +#define NGHTTP3_VERSION_NUM 0x010b00 #endif /* !defined(NGHTTP3_VERSION_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c index aae075a73cc4be..57cf744680e0a2 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c @@ -50,6 +50,10 @@ size_t nghttp3_buf_cap(const nghttp3_buf *buf) { return (size_t)(buf->end - buf->begin); } +size_t nghttp3_buf_offset(const nghttp3_buf *buf) { + return (size_t)(buf->pos - buf->begin); +} + void nghttp3_buf_reset(nghttp3_buf *buf) { buf->pos = buf->last = buf->begin; } int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem) { @@ -87,4 +91,12 @@ void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, nghttp3_buf_type type) { tbuf->buf = *buf; tbuf->type = type; + tbuf->buf.begin = tbuf->buf.pos; +} + +void nghttp3_typed_buf_shared_init(nghttp3_typed_buf *tbuf, + const nghttp3_buf *chunk) { + tbuf->buf = *chunk; + tbuf->type = NGHTTP3_BUF_TYPE_SHARED; + tbuf->buf.begin = tbuf->buf.pos = tbuf->buf.last; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h index 9fa067de91b949..abc59b92a97a73 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h @@ -42,6 +42,12 @@ void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len); */ size_t nghttp3_buf_cap(const nghttp3_buf *buf); +/* + * nghttp3_buf_offset returns the distance from tbuf->begin to + * tbuf->pos. In other words, it returns buf->pos - buf->begin. + */ +size_t nghttp3_buf_offset(const nghttp3_buf *buf); + int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem); /* @@ -57,8 +63,12 @@ typedef enum nghttp3_buf_type { memory. */ NGHTTP3_BUF_TYPE_SHARED, /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a - memory which comes from outside of the library. */ + memory which comes from outside of the library. When + acknowledged, acked_data callback is called. */ NGHTTP3_BUF_TYPE_ALIEN, + /* NGHTTP3_BUF_TYPE_ALIEN_NO_ACK is like NGHTTP3_BUF_TYPE_ALIEN, but + acked_data callback is not called. */ + NGHTTP3_BUF_TYPE_ALIEN_NO_ACK, } nghttp3_buf_type; typedef struct nghttp3_typed_buf { @@ -69,6 +79,13 @@ typedef struct nghttp3_typed_buf { void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, nghttp3_buf_type type); +/* + * nghttp3_typed_buf_shared_init initializes |tbuf| of type + * NGHTTP3_BUF_TYPE_SHARED. + */ +void nghttp3_typed_buf_shared_init(nghttp3_typed_buf *tbuf, + const nghttp3_buf *chunk); + void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf); #endif /* !defined(NGHTTP3_BUF_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c new file mode 100644 index 00000000000000..5db8376ad229dd --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c @@ -0,0 +1,75 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_callbacks.h" + +#include +#include + +#include "nghttp3_unreachable.h" + +static void callbacks_copy(nghttp3_callbacks *dest, + const nghttp3_callbacks *src, + int callbacks_version) { + assert(callbacks_version != NGHTTP3_CALLBACKS_VERSION); + + memcpy(dest, src, nghttp3_callbackslen_version(callbacks_version)); +} + +const nghttp3_callbacks * +nghttp3_callbacks_convert_to_latest(nghttp3_callbacks *dest, + int callbacks_version, + const nghttp3_callbacks *src) { + if (callbacks_version == NGHTTP3_CALLBACKS_VERSION) { + return src; + } + + memset(dest, 0, sizeof(*dest)); + + callbacks_copy(dest, src, callbacks_version); + + return dest; +} + +void nghttp3_callbacks_convert_to_old(int callbacks_version, + nghttp3_callbacks *dest, + const nghttp3_callbacks *src) { + assert(callbacks_version != NGHTTP3_CALLBACKS_VERSION); + + callbacks_copy(dest, src, callbacks_version); +} + +size_t nghttp3_callbackslen_version(int callbacks_version) { + nghttp3_callbacks callbacks; + + switch (callbacks_version) { + case NGHTTP3_CALLBACKS_VERSION: + return sizeof(callbacks); + case NGHTTP3_CALLBACKS_V1: + return offsetof(nghttp3_callbacks, recv_settings) + + sizeof(callbacks.recv_settings); + default: + nghttp3_unreachable(); + } +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h new file mode 100644 index 00000000000000..6bebb3a98dd8ca --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h @@ -0,0 +1,74 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_CALLBACKS_H +#define NGHTTP3_CALLBACKS_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +/* + * nghttp3_callbacks_convert_to_latest converts |src| of version + * |callbacks_version| to the latest version + * NGHTTP3_CALLBACKS_VERSION. + * + * |dest| must point to the latest version. |src| may be the older + * version, and if so, it may have fewer fields. Accessing those + * fields causes undefined behavior. + * + * If |callbacks_version| == NGHTTP3_CALLBACKS_VERSION, no conversion + * is made, and |src| is returned. Otherwise, first |dest| is + * zero-initialized, and then all valid fields in |src| are copied + * into |dest|. Finally, |dest| is returned. + */ +const nghttp3_callbacks *nghttp3_callbacks_convert_to_latest( + nghttp3_callbacks *dest, int callbacks_version, const nghttp3_callbacks *src); + +/* + * nghttp3_callbacks_convert_to_old converts |src| of the latest + * version to |dest| of version |callbacks_version|. + * + * |callbacks_version| must not be the latest version + * NGHTTP3_CALLBACKS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version + * |callbacks_version| from |src| to |dest|. + */ +void nghttp3_callbacks_convert_to_old(int callbacks_version, + nghttp3_callbacks *dest, + const nghttp3_callbacks *src); + +/* + * nghttp3_callbackslen_version returns the effective length of + * nghttp3_callbacks at the version |callbacks_version|. + */ +size_t nghttp3_callbackslen_version(int callbacks_version); + +#endif /* !defined(NGHTTP3_CALLBACKS_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c index f70b4f5472de64..8c0e94dc768a29 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c @@ -34,12 +34,10 @@ #include "nghttp3_conv.h" #include "nghttp3_http.h" #include "nghttp3_unreachable.h" +#include "nghttp3_settings.h" +#include "nghttp3_callbacks.h" -/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the - dynamic table capacity that QPACK encoder is willing to use. */ -#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096 - -nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent); +nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent) /* * conn_remote_stream_uni returns nonzero if |stream_id| is remote @@ -207,6 +205,32 @@ static int conn_call_recv_settings(nghttp3_conn *conn) { return 0; } +static int conn_call_recv_origin(nghttp3_conn *conn, const uint8_t *origin, + size_t originlen) { + if (!conn->callbacks.recv_origin) { + return 0; + } + + if (conn->callbacks.recv_origin(conn, origin, originlen, conn->user_data) != + 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_origin(nghttp3_conn *conn) { + if (!conn->callbacks.end_origin) { + return 0; + } + + if (conn->callbacks.end_origin(conn, conn->user_data) != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static int ricnt_less(const nghttp3_pq_entry *lhsx, const nghttp3_pq_entry *rhsx) { nghttp3_stream *lhs = @@ -233,11 +257,22 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, const nghttp3_callbacks *callbacks, int settings_version, const nghttp3_settings *settings, const nghttp3_mem *mem, void *user_data) { - int rv; nghttp3_conn *conn; + nghttp3_settings settings_latest; + nghttp3_callbacks callbacks_latest; + uint64_t map_seed; size_t i; (void)callbacks_version; - (void)settings_version; + + settings = nghttp3_settings_convert_to_latest(&settings_latest, + settings_version, settings); + callbacks = nghttp3_callbacks_convert_to_latest(&callbacks_latest, + callbacks_version, callbacks); + + assert(settings->max_field_section_size <= NGHTTP3_VARINT_MAX); + assert(settings->qpack_max_dtable_capacity <= NGHTTP3_VARINT_MAX); + assert(settings->qpack_encoder_max_dtable_capacity <= NGHTTP3_VARINT_MAX); + assert(settings->qpack_blocked_streams <= NGHTTP3_VARINT_MAX); if (mem == NULL) { mem = nghttp3_mem_default(); @@ -252,20 +287,19 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem); nghttp3_objalloc_stream_init(&conn->stream_objalloc, 8, mem); - nghttp3_map_init(&conn->streams, mem); - - rv = - nghttp3_qpack_decoder_init(&conn->qdec, settings->qpack_max_dtable_capacity, - settings->qpack_blocked_streams, mem); - if (rv != 0) { - goto qdec_init_fail; + if (callbacks->rand) { + callbacks->rand((uint8_t *)&map_seed, sizeof(map_seed)); + } else { + map_seed = 0; } - rv = nghttp3_qpack_encoder_init( - &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem); - if (rv != 0) { - goto qenc_init_fail; - } + nghttp3_map_init(&conn->streams, map_seed, mem); + + nghttp3_qpack_decoder_init(&conn->qdec, settings->qpack_max_dtable_capacity, + settings->qpack_blocked_streams, mem); + + nghttp3_qpack_encoder_init( + &conn->qenc, settings->qpack_encoder_max_dtable_capacity, ++map_seed, mem); nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem); @@ -277,8 +311,14 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, conn->callbacks = *callbacks; conn->local.settings = *settings; - if (!server) { + if (server) { + if (settings->origin_list) { + conn->local.settings.origin_list = &conn->local.origin_list; + conn->local.origin_list = *settings->origin_list; + } + } else { conn->local.settings.enable_connect_protocol = 0; + conn->local.settings.origin_list = NULL; } nghttp3_settings_default(&conn->remote.settings); conn->mem = mem; @@ -291,16 +331,6 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, *pconn = conn; return 0; - -qenc_init_fail: - nghttp3_qpack_decoder_free(&conn->qdec); -qdec_init_fail: - nghttp3_map_free(&conn->streams); - nghttp3_objalloc_free(&conn->stream_objalloc); - nghttp3_objalloc_free(&conn->out_chunk_objalloc); - nghttp3_mem_free(mem, conn); - - return rv; } int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, @@ -374,6 +404,8 @@ void nghttp3_conn_del(nghttp3_conn *conn) { nghttp3_objalloc_free(&conn->stream_objalloc); nghttp3_objalloc_free(&conn->out_chunk_objalloc); + nghttp3_mem_free(conn->mem, conn->rx.originbuf); + nghttp3_mem_free(conn->mem, conn); } @@ -399,6 +431,9 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, size_t bidi_nproc; int rv; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { /* TODO Assert idtr */ @@ -434,6 +469,10 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, return rv; } } + } else if (!nghttp3_client_stream_uni(stream_id)) { + /* server does not expect to receive new server initiated + bidirectional or unidirectional stream from client. */ + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; } else { /* unidirectional stream */ if (srclen == 0 && fin) { @@ -448,7 +487,7 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; - } else if (nghttp3_stream_uni(stream_id)) { + } else if (nghttp3_server_stream_uni(stream_id)) { if (srclen == 0 && fin) { return 0; } @@ -461,17 +500,16 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; } else { - /* client doesn't expect to receive new bidirectional stream - from server. */ + /* client doesn't expect to receive new bidirectional stream or + client initiated unidirectional stream from server. */ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; } } else if (conn->server) { - if (nghttp3_client_stream_bidi(stream_id)) { - if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) { - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; - stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; - } - } + assert(nghttp3_client_stream_bidi(stream_id) || + nghttp3_client_stream_uni(stream_id)); + } else { + assert(nghttp3_client_stream_bidi(stream_id) || + nghttp3_server_stream_uni(stream_id)); } if (srclen == 0 && !fin) { @@ -608,6 +646,9 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, break; case NGHTTP3_STREAM_TYPE_UNKNOWN: nconsumed = (nghttp3_ssize)srclen; + if (fin) { + break; + } rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR); if (rv != 0) { @@ -625,6 +666,11 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, return nread + nconsumed; } +static void conn_reset_rx_originlen(nghttp3_conn *conn) { + conn->rx.originlen_offset = 0; + conn->rx.originlen = 0; +} + static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) { return (int64_t)len >= rstate->left; } @@ -661,11 +707,11 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - rstate->fr.hd.type = rvint->acc; + rstate->fr.type = rvint->acc; nghttp3_varint_read_state_reset(rvint); rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH; if (p == end) { - break; + return (nghttp3_ssize)nconsumed; } /* Fall through */ case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH: @@ -681,19 +727,19 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - rstate->left = rstate->fr.hd.length = rvint->acc; + rstate->left = rvint->acc; nghttp3_varint_read_state_reset(rvint); if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) { - if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) { + if (rstate->fr.type != NGHTTP3_FRAME_SETTINGS) { return NGHTTP3_ERR_H3_MISSING_SETTINGS; } conn->flags |= NGHTTP3_CONN_FLAG_SETTINGS_RECVED; - } else if (rstate->fr.hd.type == NGHTTP3_FRAME_SETTINGS) { + } else if (rstate->fr.type == NGHTTP3_FRAME_SETTINGS) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } - switch (rstate->fr.hd.type) { + switch (rstate->fr.type) { case NGHTTP3_FRAME_SETTINGS: /* SETTINGS frame might be empty. */ if (rstate->left == 0) { @@ -734,6 +780,31 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: /* We do not support push */ return NGHTTP3_ERR_H3_ID_ERROR; + case NGHTTP3_FRAME_ORIGIN: + if (conn->server || + (!conn->callbacks.recv_origin && !conn->callbacks.end_origin)) { + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } + + /* ORIGIN frame might be empty */ + if (rstate->left == 0) { + rv = conn_call_end_origin(conn); + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + + break; + } + + conn_reset_rx_originlen(conn); + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN; + + break; case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */ case NGHTTP3_FRAME_DATA: case NGHTTP3_FRAME_HEADERS: @@ -971,8 +1042,14 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, break; } + conn->rx.pri_fieldbuflen = 0; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE; + if (p == end) { + return (nghttp3_ssize)nconsumed; + } + /* Fall through */ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE: /* We need to buffer Priority Field Value because it might be @@ -1027,9 +1104,134 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return rv; } - conn->rx.pri_fieldbuflen = 0; + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN: + /* client side only */ + len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); + + assert(len > 0); + + for (;;) { + nread = 0; + + for (; conn->rx.originlen_offset < sizeof(conn->rx.originlen) && + (size_t)nread < len; + ++conn->rx.originlen_offset, ++nread) { + conn->rx.originlen <<= 8; + conn->rx.originlen += *p++; + } + + nconsumed += (size_t)nread; + rstate->left -= nread; + len -= (size_t)nread; + + if (conn->rx.originlen_offset < sizeof(conn->rx.originlen)) { + /* Needs another byte to parse Origin-Len */ + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + return (nghttp3_ssize)nconsumed; + } + + if (conn->rx.originlen == 0 || rstate->left < conn->rx.originlen) { + /* While this seems OK in very loose RFC, allowing this + sounds like an atack vector. */ + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + if (len < conn->rx.originlen) { + /* ASCII-Origin does not fit into this buffer. Needs + buffering. */ + if (conn->rx.originbuf == NULL) { + conn->rx.originbuf = nghttp3_mem_malloc(conn->mem, UINT16_MAX); + if (conn->rx.originbuf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + } + + memcpy(conn->rx.originbuf, p, len); + + /* No need to update p because we will return very soon. */ + nconsumed += len; + rstate->left -= (int64_t)len; + + conn->rx.originbuflen = len; + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN; + + return (nghttp3_ssize)nconsumed; + } + + rv = conn_call_recv_origin(conn, p, conn->rx.originlen); + if (rv != 0) { + return rv; + } + + p += conn->rx.originlen; + nconsumed += conn->rx.originlen; + rstate->left -= conn->rx.originlen; + + if (rstate->left == 0) { + rv = conn_call_end_origin(conn); + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + + break; + } + + len -= conn->rx.originlen; + + conn_reset_rx_originlen(conn); + + if (p == end) { + return (nghttp3_ssize)nconsumed; + } + } + + break; + case NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN: + /* client side only */ + len = nghttp3_min_size(conn->rx.originlen - conn->rx.originbuflen, + (size_t)(end - p)); + + assert(len > 0); + + memcpy(conn->rx.originbuf + conn->rx.originbuflen, p, len); + + conn->rx.originbuflen += len; + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (conn->rx.originbuflen < conn->rx.originlen) { + return (nghttp3_ssize)nconsumed; + } + + rv = conn_call_recv_origin(conn, conn->rx.originbuf, conn->rx.originlen); + if (rv != 0) { + return rv; + } + + if (rstate->left) { + conn_reset_rx_originlen(conn); + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN; + + break; + } + + rv = conn_call_end_origin(conn); + if (rv != 0) { + return rv; + } nghttp3_stream_read_state_reset(rstate); + break; case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); @@ -1251,7 +1453,7 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, goto almost_done; } - rstate->fr.hd.type = rvint->acc; + rstate->fr.type = rvint->acc; nghttp3_varint_read_state_reset(rvint); rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH; if (p == end) { @@ -1271,10 +1473,10 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, goto almost_done; } - rstate->left = rstate->fr.hd.length = rvint->acc; + rstate->left = rvint->acc; nghttp3_varint_read_state_reset(rvint); - switch (rstate->fr.hd.type) { + switch (rstate->fr.type) { case NGHTTP3_FRAME_DATA: rv = nghttp3_stream_transit_rx_http_state( stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); @@ -1374,10 +1576,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nread = nghttp3_conn_on_headers(conn, stream, p, len, (int64_t)len == rstate->left); if (nread < 0) { - if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { - goto http_header_error; - } - return nread; } @@ -1416,10 +1614,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, } if (rv != 0) { - if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { - goto http_header_error; - } - return rv; } @@ -1458,24 +1652,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nghttp3_stream_read_state_reset(rstate); - break; - - http_header_error: - stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR; - - busy = 1; - rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST; - - rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); - if (rv != 0) { - return rv; - } - - rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); - if (rv != 0) { - return rv; - } - break; case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); @@ -1792,6 +1968,8 @@ conn_on_priority_update_stream(nghttp3_conn *conn, stream->node.pri = fr->pri; stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; return 0; } @@ -1808,7 +1986,7 @@ conn_on_priority_update_stream(nghttp3_conn *conn, int nghttp3_conn_on_priority_update(nghttp3_conn *conn, const nghttp3_frame_priority_update *fr) { assert(conn->server); - assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE); + assert(fr->type == NGHTTP3_FRAME_PRIORITY_UPDATE); return conn_on_priority_update_stream(conn, fr); } @@ -1836,7 +2014,7 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, nghttp3_stream *stream; int rv; nghttp3_stream_callbacks callbacks = { - conn_stream_acked_data, + .acked_data = conn_stream_acked_data, }; rv = nghttp3_stream_new(&stream, stream_id, &callbacks, @@ -1874,6 +2052,8 @@ int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_frame_entry frent; int rv; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); assert(!conn->server || nghttp3_server_stream_uni(stream_id)); assert(conn->server || nghttp3_client_stream_uni(stream_id)); @@ -1895,10 +2075,27 @@ int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { return rv; } - frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS; + frent.fr.type = NGHTTP3_FRAME_SETTINGS; frent.aux.settings.local_settings = &conn->local.settings; - return nghttp3_stream_frq_add(stream, &frent); + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + return rv; + } + + if (conn->local.settings.origin_list) { + assert(conn->server); + + frent.fr.origin.type = NGHTTP3_FRAME_ORIGIN; + frent.fr.origin.origin_list = *conn->local.settings.origin_list; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + return rv; + } + } + + return 0; } int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, @@ -1906,6 +2103,10 @@ int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, nghttp3_stream *stream; int rv; + assert(qenc_stream_id >= 0); + assert(qenc_stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + assert(qdec_stream_id >= 0); + assert(qdec_stream_id <= (int64_t)NGHTTP3_MAX_VARINT); assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id)); assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id)); assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id)); @@ -2118,14 +2319,14 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, const nghttp3_data_reader *dr) { int rv; nghttp3_nv *nnva; - nghttp3_frame_entry frent = {0}; + nghttp3_frame_entry frent; rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); if (rv != 0) { return rv; } - frent.fr.hd.type = NGHTTP3_FRAME_HEADERS; + frent.fr.type = NGHTTP3_FRAME_HEADERS; frent.fr.headers.nva = nnva; frent.fr.headers.nvlen = nvlen; @@ -2136,7 +2337,7 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, } if (dr) { - frent.fr.hd.type = NGHTTP3_FRAME_DATA; + frent.fr.type = NGHTTP3_FRAME_DATA; frent.aux.data.dr = *dr; rv = nghttp3_stream_frq_add(stream, &frent); @@ -2194,13 +2395,11 @@ int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, assert(!conn->server); assert(conn->tx.qenc); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); assert(nghttp3_client_stream_bidi(stream_id)); - /* TODO Should we check that stream_id is client stream_id? */ /* TODO Check GOAWAY last stream ID */ - if (nghttp3_stream_uni(stream_id)) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) { return NGHTTP3_ERR_CONN_CLOSING; @@ -2288,12 +2487,12 @@ int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id, } int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { - nghttp3_frame_entry frent = {0}; + nghttp3_frame_entry frent; int rv; assert(conn->tx.ctrl); - frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; + frent.fr.type = NGHTTP3_FRAME_GOAWAY; frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID; @@ -2311,12 +2510,12 @@ int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { } int nghttp3_conn_shutdown(nghttp3_conn *conn) { - nghttp3_frame_entry frent = {0}; + nghttp3_frame_entry frent; int rv; assert(conn->tx.ctrl); - frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; + frent.fr.type = NGHTTP3_FRAME_GOAWAY; if (conn->server) { frent.fr.goaway.id = nghttp3_min_int64((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4); @@ -2454,6 +2653,9 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (!nghttp3_client_stream_bidi(stream_id)) { return 0; } @@ -2515,6 +2717,9 @@ uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, nghttp3_stream *stream; int uni = 0; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (!nghttp3_client_stream_bidi(stream_id)) { uni = conn_remote_stream_uni(conn, stream_id); if (!uni) { @@ -2542,6 +2747,8 @@ int nghttp3_conn_get_stream_priority_versioned(nghttp3_conn *conn, (void)pri_version; assert(conn->server); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); if (!nghttp3_client_stream_bidi(stream_id)) { return NGHTTP3_ERR_INVALID_ARGUMENT; @@ -2562,10 +2769,12 @@ int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn, const uint8_t *data, size_t datalen) { nghttp3_stream *stream; - nghttp3_frame_entry frent = {0}; + nghttp3_frame_entry frent; uint8_t *buf = NULL; assert(!conn->server); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); if (!nghttp3_client_stream_bidi(stream_id)) { return NGHTTP3_ERR_INVALID_ARGUMENT; @@ -2585,7 +2794,7 @@ int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn, memcpy(buf, data, datalen); } - frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE; + frent.fr.type = NGHTTP3_FRAME_PRIORITY_UPDATE; frent.fr.priority_update.pri_elem_id = stream_id; frent.fr.priority_update.data = buf; frent.fr.priority_update.datalen = datalen; @@ -2603,6 +2812,8 @@ int nghttp3_conn_set_server_stream_priority_versioned(nghttp3_conn *conn, assert(conn->server); assert(pri->urgency < NGHTTP3_URGENCY_LEVELS); assert(pri->inc == 0 || pri->inc == 1); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); if (!nghttp3_client_stream_bidi(stream_id)) { return NGHTTP3_ERR_INVALID_ARGUMENT; @@ -2626,13 +2837,3 @@ int nghttp3_conn_is_drained(nghttp3_conn *conn) { nghttp3_stream_outq_write_done(conn->tx.ctrl) && nghttp3_ringbuf_len(&conn->tx.ctrl->frq) == 0; } - -void nghttp3_settings_default_versioned(int settings_version, - nghttp3_settings *settings) { - (void)settings_version; - - memset(settings, 0, sizeof(nghttp3_settings)); - settings->max_field_section_size = NGHTTP3_VARINT_MAX; - settings->qpack_encoder_max_dtable_capacity = - NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY; -} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h index 1218ba508ba46a..80c26f80322c60 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h @@ -38,8 +38,6 @@ #include "nghttp3_idtr.h" #include "nghttp3_gaptr.h" -#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1) - /* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic table size for QPACK encoder. */ #define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384 @@ -76,7 +74,7 @@ typedef struct nghttp3_chunk { nghttp3_opl_entry oplent; } nghttp3_chunk; -nghttp3_objalloc_decl(chunk, nghttp3_chunk, oplent); +nghttp3_objalloc_decl(chunk, nghttp3_chunk, oplent) struct nghttp3_conn { nghttp3_objalloc out_chunk_objalloc; @@ -95,6 +93,11 @@ struct nghttp3_conn { uint16_t flags; struct { + /* origin_list contains the shallow copy of + nghttp3_settings.origin_list passed from an application if this + object is initialized as server. settings.origin_list may + point to the address of this field. */ + nghttp3_vec origin_list; nghttp3_settings settings; struct { /* max_pushes is the number of push IDs that local endpoint can @@ -125,12 +128,36 @@ struct nghttp3_conn { int64_t max_stream_id_bidi; - /* pri_fieldbuf is a buffer to store incoming Priority Field Value - in PRIORITY_UPDATE frame. */ - uint8_t pri_fieldbuf[8]; - /* pri_fieldlen is the number of bytes written into - pri_fieldbuf. */ - size_t pri_fieldbuflen; + union { + struct { + /* pri_fieldbuf is a buffer to store incoming Priority Field Value + in PRIORITY_UPDATE frame. */ + uint8_t pri_fieldbuf[8]; + /* pri_fieldlen is the number of bytes written into + pri_fieldbuf. */ + size_t pri_fieldbuflen; + }; + /* ORIGIN frame */ + struct { + /* originlen_offset is the offset to Origin-Len that is going + to be added to originlen. If this value equals + sizeof(originlen), Origin-Len is fully read, and the length + of ASCII-Origin is determined. */ + size_t originlen_offset; + /* originlen is Origin-Len of ASCII-Origin currently read. */ + uint16_t originlen; + }; + }; + + /* originbuf points to the buffer that contains ASCII-Origin that + is not fully available in a single input buffer. If it is + fully available in the input buffer, it is emitted to an + application without using this field. Otherwise, partial + ASCII-Origin is copied to this field, and the complete + ASCII-Origin is emitted when the assembly finishes. */ + uint8_t *originbuf; + /* originbuflen is the length of bytes written to originbuf. */ + size_t originbuflen; } rx; struct { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c index 6439a6d782960c..9b32ca3f405b9f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c @@ -66,8 +66,6 @@ const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p) { } } -int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } - size_t nghttp3_get_varintlen(const uint8_t *p) { return (size_t)(1u << (*p >> 6)); } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h index 40f5d4de782883..8895fe8d3415d6 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h @@ -56,6 +56,8 @@ #include +#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1) + #if HAVE_DECL_BE64TOH # define nghttp3_ntohl64(N) be64toh(N) # define nghttp3_htonl64(N) htobe64(N) @@ -135,12 +137,6 @@ STIN uint16_t ntohs(uint16_t netshort) { */ const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p); -/* - * nghttp3_get_varint_fb reads first byte of encoded variable-length - * integer from |p|. - */ -int64_t nghttp3_get_varint_fb(const uint8_t *p); - /* * nghttp3_get_varintlen returns the required number of bytes to read * variable-length integer starting at |p|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c index 1d87e448d887cf..da88b54f96de65 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c @@ -31,21 +31,22 @@ #include "nghttp3_conv.h" #include "nghttp3_str.h" -uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) { - p = nghttp3_put_varint(p, hd->type); - p = nghttp3_put_varint(p, hd->length); +uint8_t *nghttp3_frame_write_hd(uint8_t *p, int64_t type, int64_t payloadlen) { + p = nghttp3_put_varint(p, type); + p = nghttp3_put_varint(p, payloadlen); return p; } -size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) { - return nghttp3_put_varintlen(hd->type) + nghttp3_put_varintlen(hd->length); +size_t nghttp3_frame_write_hd_len(int64_t type, int64_t payloadlen) { + return nghttp3_put_varintlen(type) + nghttp3_put_varintlen(payloadlen); } uint8_t *nghttp3_frame_write_settings(uint8_t *p, - const nghttp3_frame_settings *fr) { + const nghttp3_frame_settings *fr, + int64_t payloadlen) { size_t i; - p = nghttp3_frame_write_hd(p, &fr->hd); + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); for (i = 0; i < fr->niv; ++i) { p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id); @@ -71,9 +72,9 @@ size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen, nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; } -uint8_t *nghttp3_frame_write_goaway(uint8_t *p, - const nghttp3_frame_goaway *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); +uint8_t *nghttp3_frame_write_goaway(uint8_t *p, const nghttp3_frame_goaway *fr, + int64_t payloadlen) { + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); p = nghttp3_put_varint(p, fr->id); return p; @@ -89,10 +90,9 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; } -uint8_t * -nghttp3_frame_write_priority_update(uint8_t *p, - const nghttp3_frame_priority_update *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); +uint8_t *nghttp3_frame_write_priority_update( + uint8_t *p, const nghttp3_frame_priority_update *fr, int64_t payloadlen) { + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); p = nghttp3_put_varint(p, fr->pri_elem_id); if (fr->datalen) { p = nghttp3_cpymem(p, fr->data, fr->datalen); @@ -107,7 +107,27 @@ size_t nghttp3_frame_write_priority_update_len( *ppayloadlen = (int64_t)payloadlen; - return nghttp3_put_varintlen(fr->hd.type) + + return nghttp3_put_varintlen(fr->type) + + nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_origin(uint8_t *p, const nghttp3_frame_origin *fr, + int64_t payloadlen) { + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); + if (fr->origin_list.len) { + p = nghttp3_cpymem(p, fr->origin_list.base, fr->origin_list.len); + } + + return p; +} + +size_t nghttp3_frame_write_origin_len(int64_t *ppayloadlen, + const nghttp3_frame_origin *fr) { + size_t payloadlen = fr->origin_list.len; + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varintlen(fr->type) + nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h index e216967d740b86..cca7ed0d6c95c7 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h @@ -44,6 +44,8 @@ /* PRIORITY_UPDATE: https://datatracker.ietf.org/doc/html/rfc9218 */ #define NGHTTP3_FRAME_PRIORITY_UPDATE 0x0f0700 #define NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID 0x0f0701 +/* ORIGIN: https://datatracker.ietf.org/doc/html/rfc9412 */ +#define NGHTTP3_FRAME_ORIGIN 0x0c /* Frame types that are reserved for HTTP/2, and must not be used in HTTP/3. */ @@ -52,17 +54,12 @@ #define NGHTTP3_H2_FRAME_WINDOW_UPDATE 0x08 #define NGHTTP3_H2_FRAME_CONTINUATION 0x9 -typedef struct nghttp3_frame_hd { - int64_t type; - int64_t length; -} nghttp3_frame_hd; - typedef struct nghttp3_frame_data { - nghttp3_frame_hd hd; + int64_t type; } nghttp3_frame_data; typedef struct nghttp3_frame_headers { - nghttp3_frame_hd hd; + int64_t type; nghttp3_nv *nva; size_t nvlen; } nghttp3_frame_headers; @@ -84,20 +81,20 @@ typedef struct nghttp3_settings_entry { } nghttp3_settings_entry; typedef struct nghttp3_frame_settings { - nghttp3_frame_hd hd; + int64_t type; size_t niv; nghttp3_settings_entry iv[1]; } nghttp3_frame_settings; typedef struct nghttp3_frame_goaway { - nghttp3_frame_hd hd; + int64_t type; int64_t id; } nghttp3_frame_goaway; typedef struct nghttp3_frame_priority_update { - nghttp3_frame_hd hd; - /* pri_elem_id is stream ID if hd.type == - NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type == + int64_t type; + /* pri_elem_id is stream ID if type == + NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if type == NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined otherwise. */ int64_t pri_elem_id; @@ -114,42 +111,54 @@ typedef struct nghttp3_frame_priority_update { }; } nghttp3_frame_priority_update; +typedef struct nghttp3_frame_origin { + int64_t type; + /* These fields are only used by server to send ORIGIN frame. + Client never use them. */ + nghttp3_vec origin_list; +} nghttp3_frame_origin; + typedef union nghttp3_frame { - nghttp3_frame_hd hd; + int64_t type; nghttp3_frame_data data; nghttp3_frame_headers headers; nghttp3_frame_settings settings; nghttp3_frame_goaway goaway; nghttp3_frame_priority_update priority_update; + nghttp3_frame_origin origin; } nghttp3_frame; /* - * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This - * function assumes that |dest| has enough space to write |hd|. + * nghttp3_frame_write_hd writes frame header consisting of |type| and + * |payloadlen| to |dest|. This function assumes that |dest| has + * enough space to write the frame header. * * This function returns |dest| plus the number of bytes written. */ -uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd); +uint8_t *nghttp3_frame_write_hd(uint8_t *dest, int64_t type, + int64_t payloadlen); /* * nghttp3_frame_write_hd_len returns the number of bytes required to - * write |hd|. hd->length must be set. + * write a frame header consisting of |type| and |payloadlen|. */ -size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd); +size_t nghttp3_frame_write_hd_len(int64_t type, int64_t payloadlen); /* * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|. * This function assumes that |dest| has enough space to write |fr|. + * |payloadlen| is the length of the frame payload. * * This function returns |dest| plus the number of bytes written. */ uint8_t *nghttp3_frame_write_settings(uint8_t *dest, - const nghttp3_frame_settings *fr); + const nghttp3_frame_settings *fr, + int64_t payloadlen); /* * nghttp3_frame_write_settings_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. + * required to write |fr|. This function stores the frame payload + * length in |*ppayloadlen|. */ size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, const nghttp3_frame_settings *fr); @@ -157,16 +166,18 @@ size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, /* * nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|. * This function assumes that |dest| has enough space to write |fr|. + * |payloadlen| is the length of the frame payload. * * This function returns |dest| plus the number of bytes written. */ uint8_t *nghttp3_frame_write_goaway(uint8_t *dest, - const nghttp3_frame_goaway *fr); + const nghttp3_frame_goaway *fr, + int64_t payloadlen); /* * nghttp3_frame_write_goaway_len returns the number of bytes required - * to write |fr|. fr->hd.length is ignored. This function stores - * payload length in |*ppayloadlen|. + * to write |fr|. This function stores the frame payload length in + * |*ppayloadlen|. */ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, const nghttp3_frame_goaway *fr); @@ -174,22 +185,40 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, /* * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame * |fr| to |dest|. This function assumes that |dest| has enough space - * to write |fr|. + * to write |fr|. |payloadlen| is the length of the frame payload. * * This function returns |dest| plus the number of bytes written; */ -uint8_t * -nghttp3_frame_write_priority_update(uint8_t *dest, - const nghttp3_frame_priority_update *fr); +uint8_t *nghttp3_frame_write_priority_update( + uint8_t *dest, const nghttp3_frame_priority_update *fr, int64_t payloadlen); /* * nghttp3_frame_write_priority_update_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. + * required to write |fr|. This function stores the frame payload + * length in |*ppayloadlen|. */ size_t nghttp3_frame_write_priority_update_len( int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr); +/* + * nghttp3_frame_write_origin writes ORIGIN frame |fr| to |dest|. + * This function assumes that |dest| has enough space to write |fr|. + * |payloadlen| is the length of the frame payload. + * + * This function returns |dest| plus the number of bytes written; + */ +uint8_t *nghttp3_frame_write_origin(uint8_t *dest, + const nghttp3_frame_origin *fr, + int64_t payloadlen); + +/* + * nghttp3_frame_write_origin_len returns the number of bytes required + * to write |fr|. This function stores the frame payload length in + * |*ppayloadlen|. + */ +size_t nghttp3_frame_write_origin_len(int64_t *ppayloadlen, + const nghttp3_frame_origin *fr); + /* * nghttp3_nva_copy copies name/value pairs from |nva|, which contains * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c index 20eed5faa2bcba..e8ab509c8f9a11 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c @@ -29,14 +29,16 @@ #include void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { - nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range), - mem); + nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, + nghttp3_ksl_range_search, sizeof(nghttp3_range), mem); gaptr->mem = mem; } static int gaptr_gap_init(nghttp3_gaptr *gaptr) { - nghttp3_range range = {0, UINT64_MAX}; + nghttp3_range range = { + .end = UINT64_MAX, + }; return nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL); } @@ -52,7 +54,11 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) { int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen) { int rv; - nghttp3_range k, m, l, r, q = {offset, offset + datalen}; + nghttp3_range k, m, l, r; + nghttp3_range q = { + .begin = offset, + .end = offset + datalen, + }; nghttp3_ksl_it it; if (nghttp3_ksl_len(&gaptr->gap) == 0) { @@ -62,8 +68,8 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, } } - it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_search); for (; !nghttp3_ksl_it_end(&it);) { k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); @@ -112,7 +118,10 @@ uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, uint64_t offset) { - nghttp3_range q = {offset, offset + 1}; + nghttp3_range q = { + .begin = offset, + .end = offset + 1, + }; nghttp3_ksl_it it; if (nghttp3_ksl_len(&gaptr->gap) == 0) { @@ -120,8 +129,8 @@ nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, return r; } - it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_search); assert(!nghttp3_ksl_it_end(&it)); @@ -130,7 +139,10 @@ nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen) { - nghttp3_range q = {offset, offset + datalen}; + nghttp3_range q = { + .begin = offset, + .end = offset + datalen, + }; nghttp3_ksl_it it; nghttp3_range m; @@ -138,8 +150,11 @@ int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, return 0; } - it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_search); + + assert(!nghttp3_ksl_it_end(&it)); + m = nghttp3_range_intersect(&q, (nghttp3_range *)nghttp3_ksl_it_key(&it)); return nghttp3_range_len(&m) == 0; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c index 38092cfb7c322c..9a8f06ea11da9b 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c @@ -69,11 +69,11 @@ static int64_t parse_uint(const uint8_t *s, size_t len) { } for (i = 0; i < len; ++i) { if ('0' <= s[i] && s[i] <= '9') { - if (n > INT64_MAX / 10) { + if (n > (int64_t)NGHTTP3_MAX_VARINT / 10) { return -1; } n *= 10; - if (n > INT64_MAX - (s[i] - '0')) { + if (n > (int64_t)NGHTTP3_MAX_VARINT - (s[i] - '0')) { return -1; } n += s[i] - '0'; @@ -124,17 +124,17 @@ static int is_ws(uint8_t c) { int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, size_t valuelen) { nghttp3_pri pri = *dest; - sf_parser sfp; - sf_vec key; - sf_value val; + sfparse_parser sfp; + sfparse_vec key; + sfparse_value val; int rv; - sf_parser_init(&sfp, value, valuelen); + sfparse_parser_init(&sfp, value, valuelen); for (;;) { - rv = sf_parser_dict(&sfp, &key, &val); + rv = sfparse_parser_dict(&sfp, &key, &val); if (rv != 0) { - if (rv == SF_ERR_EOF) { + if (rv == SFPARSE_ERR_EOF) { break; } @@ -147,7 +147,7 @@ int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, switch (key.base[0]) { case 'i': - if (val.type != SF_TYPE_BOOLEAN) { + if (val.type != SFPARSE_TYPE_BOOLEAN) { return NGHTTP3_ERR_INVALID_ARGUMENT; } @@ -155,7 +155,8 @@ int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, break; case 'u': - if (val.type != SF_TYPE_INTEGER || val.integer < NGHTTP3_URGENCY_HIGH || + if (val.type != SFPARSE_TYPE_INTEGER || + val.integer < NGHTTP3_URGENCY_HIGH || NGHTTP3_URGENCY_LOW < val.integer) { return NGHTTP3_ERR_INVALID_ARGUMENT; } @@ -197,7 +198,7 @@ static char VALID_AUTHORITY_CHARS[] = { 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, - 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, @@ -548,6 +549,9 @@ static int http_request_on_header(nghttp3_http_state *http, break; case NGHTTP3_QPACK_TOKEN_PRIORITY: if (!nghttp3_check_header_value(nv->value->base, nv->value->len)) { + http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY; + http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; } @@ -991,7 +995,11 @@ int nghttp3_check_header_value(const uint8_t *value, size_t len) { case 0: return 1; case 1: - return !is_ws(*value); + if (is_ws(*value)) { + return 0; + } + + break; default: if (is_ws(*value) || is_ws(*(value + len - 1))) { return 0; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c index a3b5fbcb05f4f3..33dec06388a70a 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c @@ -34,9 +34,9 @@ #include "nghttp3_mem.h" #include "nghttp3_range.h" -static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; +static nghttp3_ksl_blk null_blk; -nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent); +nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent) static size_t ksl_nodelen(size_t keylen) { assert(keylen >= sizeof(uint64_t)); @@ -59,7 +59,8 @@ static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node, } void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, - size_t keylen, const nghttp3_mem *mem) { + nghttp3_ksl_search search, size_t keylen, + const nghttp3_mem *mem) { size_t nodelen = ksl_nodelen(keylen); nghttp3_objalloc_init(&ksl->blkalloc, @@ -68,6 +69,7 @@ void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, ksl->head = NULL; ksl->front = ksl->back = NULL; ksl->compar = compar; + ksl->search = search; ksl->n = 0; ksl->keylen = keylen; ksl->nodelen = nodelen; @@ -274,20 +276,6 @@ static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, ++blk->n; } -static size_t ksl_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, - const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar) { - size_t i; - nghttp3_ksl_node *node; - - for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; - i < blk->n && compar((nghttp3_ksl_key *)node->key, key); - ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) - ; - - return i; -} - int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key, void *data) { nghttp3_ksl_blk *blk; @@ -312,7 +300,7 @@ int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, blk = ksl->head; for (;;) { - i = ksl_search(ksl, blk, key, ksl->compar); + i = ksl->search(ksl, blk, key); if (blk->leaf) { if (i < blk->n && @@ -571,7 +559,7 @@ int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, } for (;;) { - i = ksl_search(ksl, blk, key, ksl->compar); + i = ksl->search(ksl, blk, key); if (i == blk->n) { if (it) { @@ -642,12 +630,12 @@ int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, nghttp3_ksl_it nghttp3_ksl_lower_bound(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key) { - return nghttp3_ksl_lower_bound_compar(ksl, key, ksl->compar); + return nghttp3_ksl_lower_bound_search(ksl, key, ksl->search); } -nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl, +nghttp3_ksl_it nghttp3_ksl_lower_bound_search(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar) { + nghttp3_ksl_search search) { nghttp3_ksl_blk *blk = ksl->head; nghttp3_ksl_it it; size_t i; @@ -658,7 +646,7 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl, } for (;;) { - i = ksl_search(ksl, blk, key, compar); + i = search(ksl, blk, key); if (blk->leaf) { if (i == blk->n && blk->next) { @@ -702,7 +690,7 @@ void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, assert(ksl->head); for (;;) { - i = ksl_search(ksl, blk, old_key, ksl->compar); + i = ksl->search(ksl, blk, old_key); assert(i < blk->n); node = nghttp3_ksl_nth_node(ksl, blk, i); @@ -825,9 +813,50 @@ int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, return a->begin < b->begin; } +nghttp3_ksl_search_def(range, nghttp3_ksl_range_compar) + +size_t nghttp3_ksl_range_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_range_search(ksl, blk, key); +} + int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) { const nghttp3_range *a = lhs, *b = rhs; return a->begin < b->begin && !(nghttp3_max_uint64(a->begin, b->begin) < nghttp3_min_uint64(a->end, b->end)); } + +nghttp3_ksl_search_def(range_exclusive, nghttp3_ksl_range_exclusive_compar) + +size_t nghttp3_ksl_range_exclusive_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_range_exclusive_search(ksl, blk, key); +} + +int nghttp3_ksl_uint64_less(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + return *(uint64_t *)lhs < *(uint64_t *)rhs; +} + +nghttp3_ksl_search_def(uint64_less, nghttp3_ksl_uint64_less) + +size_t nghttp3_ksl_uint64_less_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_uint64_less_search(ksl, blk, key); +} + +int nghttp3_ksl_int64_greater(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + return *(int64_t *)lhs > *(int64_t *)rhs; +} + +nghttp3_ksl_search_def(int64_greater, nghttp3_ksl_int64_greater) + +size_t nghttp3_ksl_int64_greater_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_int64_greater_search(ksl, blk, key); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h index e15e227ce50fb0..be7469baa99c44 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h @@ -104,7 +104,7 @@ struct nghttp3_ksl_blk { }; }; -nghttp3_objalloc_decl(ksl_blk, nghttp3_ksl_blk, oplent); +nghttp3_objalloc_decl(ksl_blk, nghttp3_ksl_blk, oplent) /* * nghttp3_ksl_compar is a function type which returns nonzero if key @@ -115,6 +115,35 @@ typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs, typedef struct nghttp3_ksl nghttp3_ksl; +/* + * nghttp3_ksl_search is a function to search for the first element in + * |blk|->nodes which is not ordered before |key|. It returns the + * index of such element. It returns |blk|->n if there is no such + * element. + */ +typedef size_t (*nghttp3_ksl_search)(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_search_def is a macro to implement nghttp3_ksl_search + * with COMPAR which is supposed to be nghttp3_ksl_compar. + */ +#define nghttp3_ksl_search_def(NAME, COMPAR) \ + static size_t ksl_##NAME##_search(const nghttp3_ksl *ksl, \ + nghttp3_ksl_blk *blk, \ + const nghttp3_ksl_key *key) { \ + size_t i; \ + nghttp3_ksl_node *node; \ + \ + for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; \ + i < blk->n && COMPAR((nghttp3_ksl_key *)node->key, key); ++i, \ + node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) \ + ; \ + \ + return i; \ + } + typedef struct nghttp3_ksl_it nghttp3_ksl_it; /* @@ -138,6 +167,7 @@ struct nghttp3_ksl { /* back points to the last leaf block. */ nghttp3_ksl_blk *back; nghttp3_ksl_compar compar; + nghttp3_ksl_search search; /* n is the number of elements stored. */ size_t n; /* keylen is the size of key */ @@ -149,11 +179,13 @@ struct nghttp3_ksl { /* * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare - * function. |keylen| is the length of key and must be at least + * function. |search| is a search function which must use |compar|. + * |keylen| is the length of key and must be at least * sizeof(uint64_t). */ void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, - size_t keylen, const nghttp3_mem *mem); + nghttp3_ksl_search search, size_t keylen, + const nghttp3_mem *mem); /* * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is @@ -218,12 +250,12 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key); /* - * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound, - * but it takes custom function |compar| to do lower bound search. + * nghttp3_ksl_lower_bound_search works like nghttp3_ksl_lower_bound, + * but it takes custom function |search| to do lower bound search. */ -nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl, +nghttp3_ksl_it nghttp3_ksl_lower_bound_search(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar); + nghttp3_ksl_search search); /* * nghttp3_ksl_update_key replaces the key of nodes which has @@ -263,8 +295,11 @@ void nghttp3_ksl_clear(nghttp3_ksl *ksl); /* * nghttp3_ksl_nth_node returns the |n|th node under |blk|. */ -#define nghttp3_ksl_nth_node(KSL, BLK, N) \ - ((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) +static inline nghttp3_ksl_node *nghttp3_ksl_nth_node(const nghttp3_ksl *ksl, + const nghttp3_ksl_blk *blk, + size_t n) { + return (nghttp3_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} #ifndef WIN32 /* @@ -286,18 +321,21 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, * |it| points to. It is undefined to call this function when * nghttp3_ksl_it_end(it) returns nonzero. */ -#define nghttp3_ksl_it_get(IT) \ - nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data +static inline void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) { + return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data; +} /* * nghttp3_ksl_it_next advances the iterator by one. It is undefined * if this function is called when nghttp3_ksl_it_end(it) returns * nonzero. */ -#define nghttp3_ksl_it_next(IT) \ - (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ - ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ - : 0) +static inline void nghttp3_ksl_it_next(nghttp3_ksl_it *it) { + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} /* * nghttp3_ksl_it_prev moves backward the iterator by one. It is @@ -310,8 +348,9 @@ void nghttp3_ksl_it_prev(nghttp3_ksl_it *it); * nghttp3_ksl_it_end returns nonzero if |it| points to the one beyond * the last node. */ -#define nghttp3_ksl_it_end(IT) \ - ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) +static inline int nghttp3_ksl_it_end(const nghttp3_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} /* * nghttp3_ksl_it_begin returns nonzero if |it| points to the first @@ -325,27 +364,75 @@ int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it); * It is undefined to call this function when nghttp3_ksl_it_end(it) * returns nonzero. */ -#define nghttp3_ksl_it_key(IT) \ - ((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) +static inline nghttp3_ksl_key *nghttp3_ksl_it_key(const nghttp3_ksl_it *it) { + return (nghttp3_ksl_key *)nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->key; +} /* * nghttp3_ksl_range_compar is an implementation of - * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to - * nghttp3_range object and the function returns nonzero if (const - * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range - * *)(rhs->ptr)->begin. + * nghttp3_ksl_compar. |lhs| and |rhs| must point to nghttp3_range + * object, and the function returns nonzero if ((const nghttp3_range + * *)lhs)->begin < ((const nghttp3_range *)rhs)->begin. */ int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs); +/* + * nghttp3_ksl_range_search is an implementation of nghttp3_ksl_search + * that uses nghttp3_ksl_range_compar. + */ +size_t nghttp3_ksl_range_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + /* * nghttp3_ksl_range_exclusive_compar is an implementation of - * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to - * nghttp3_range object and the function returns nonzero if (const - * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range - * *)(rhs->ptr)->begin and the 2 ranges do not intersect. + * nghttp3_ksl_compar. |lhs| and |rhs| must point to nghttp3_range + * object, and the function returns nonzero if ((const nghttp3_range + * *)lhs)->begin < ((const nghttp3_range *)rhs)->begin, and the 2 + * ranges do not intersect. */ int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs); +/* + * nghttp3_ksl_range_exclusive_search is an implementation of + * nghttp3_ksl_search that uses nghttp3_ksl_range_exclusive_compar. + */ +size_t nghttp3_ksl_range_exclusive_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_uint64_less is an implementation of nghttp3_ksl_compar. + * |lhs| and |rhs| must point to uint64_t objects, and the function + * returns nonzero if *(uint64_t *)|lhs| < *(uint64_t *)|rhs|. + */ +int nghttp3_ksl_uint64_less(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +/* + * nghttp3_ksl_uint64_less_search is an implementation of + * nghttp3_ksl_search that uses nghttp3_ksl_uint64_less. + */ +size_t nghttp3_ksl_uint64_less_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_int64_greater is an implementation of + * nghttp3_ksl_compar. |lhs| and |rhs| must point to int64_t objects, + * and the function returns nonzero if *(int64_t *)|lhs| > *(int64_t + * *)|rhs|. + */ +int nghttp3_ksl_int64_greater(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +/* + * nghttp3_ksl_int64_greater_search is an implementation of + * nghttp3_ksl_search that uses nghttp3_ksl_int64_greater. + */ +size_t nghttp3_ksl_int64_greater_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + #endif /* !defined(NGHTTP3_KSL_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h index a4e1dfea3cda00..ffe0ea70924327 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h @@ -48,27 +48,27 @@ #define nghttp3_max_def(SUFFIX, T) \ static inline T nghttp3_max_##SUFFIX(T a, T b) { return a < b ? b : a; } -nghttp3_max_def(int8, int8_t); -nghttp3_max_def(int16, int16_t); -nghttp3_max_def(int32, int32_t); -nghttp3_max_def(int64, int64_t); -nghttp3_max_def(uint8, uint8_t); -nghttp3_max_def(uint16, uint16_t); -nghttp3_max_def(uint32, uint32_t); -nghttp3_max_def(uint64, uint64_t); -nghttp3_max_def(size, size_t); +nghttp3_max_def(int8, int8_t) +nghttp3_max_def(int16, int16_t) +nghttp3_max_def(int32, int32_t) +nghttp3_max_def(int64, int64_t) +nghttp3_max_def(uint8, uint8_t) +nghttp3_max_def(uint16, uint16_t) +nghttp3_max_def(uint32, uint32_t) +nghttp3_max_def(uint64, uint64_t) +nghttp3_max_def(size, size_t) #define nghttp3_min_def(SUFFIX, T) \ static inline T nghttp3_min_##SUFFIX(T a, T b) { return a < b ? a : b; } -nghttp3_min_def(int8, int8_t); -nghttp3_min_def(int16, int16_t); -nghttp3_min_def(int32, int32_t); -nghttp3_min_def(int64, int64_t); -nghttp3_min_def(uint8, uint8_t); -nghttp3_min_def(uint16, uint16_t); -nghttp3_min_def(uint32, uint32_t); -nghttp3_min_def(uint64, uint64_t); -nghttp3_min_def(size, size_t); +nghttp3_min_def(int8, int8_t) +nghttp3_min_def(int16, int16_t) +nghttp3_min_def(int32, int32_t) +nghttp3_min_def(int64, int64_t) +nghttp3_min_def(uint8, uint8_t) +nghttp3_min_def(uint16, uint16_t) +nghttp3_min_def(uint32, uint32_t) +nghttp3_min_def(uint64, uint64_t) +nghttp3_min_def(size, size_t) #endif /* !defined(NGHTTP3_MACRO_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c index cc5e42a2caf63f..f3165c81e61153 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c @@ -32,12 +32,13 @@ #include "nghttp3_conv.h" -#define NGHTTP3_INITIAL_TABLE_LENBITS 4 +#define NGHTTP3_INITIAL_HASHBITS 4 -void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { +void nghttp3_map_init(nghttp3_map *map, uint64_t seed, const nghttp3_mem *mem) { map->mem = mem; map->hashbits = 0; map->table = NULL; + map->seed = seed; map->size = 0; } @@ -78,8 +79,14 @@ int nghttp3_map_each(const nghttp3_map *map, int (*func)(void *data, void *ptr), return 0; } -static size_t hash(nghttp3_map_key_type key, size_t bits) { - return (size_t)((key * 11400714819323198485llu) >> (64 - bits)); +static size_t map_hash(const nghttp3_map *map, nghttp3_map_key_type key) { + /* hasher from + https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs + We do not perform finalization here because we use top bits + anyway. */ + key += map->seed; + key *= 0xf1357aea2e62a9c5ull; + return (size_t)((key * 11400714819323198485llu) >> (64 - map->hashbits)); } static void map_bucket_swap(nghttp3_map_bucket *a, nghttp3_map_bucket *b) { @@ -110,24 +117,28 @@ void nghttp3_map_print_distance(const nghttp3_map *map) { continue; } - idx = hash(bkt->key, map->hashbits); + idx = map_hash(map, bkt->key); fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i, - hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl); + map_hash(map, bkt->key), bkt->key, idx, bkt->psl); } } #endif /* !defined(WIN32) */ -static int insert(nghttp3_map_bucket *table, size_t hashbits, - nghttp3_map_key_type key, void *data) { - size_t idx = hash(key, hashbits); - nghttp3_map_bucket b = {0, key, data}, *bkt; - size_t mask = (1u << hashbits) - 1; +static int map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) { + size_t idx = map_hash(map, key); + nghttp3_map_bucket b = { + .key = key, + .data = data, + }; + nghttp3_map_bucket *bkt; + size_t mask = (1u << map->hashbits) - 1; for (;;) { - bkt = &table[idx]; + bkt = &map->table[idx]; if (bkt->data == NULL) { *bkt = b; + ++map->size; return 0; } @@ -148,15 +159,19 @@ static int insert(nghttp3_map_bucket *table, size_t hashbits, static int map_resize(nghttp3_map *map, size_t new_hashbits) { size_t i; - nghttp3_map_bucket *new_table; nghttp3_map_bucket *bkt; size_t tablelen; int rv; + nghttp3_map new_map = { + .table = nghttp3_mem_calloc(map->mem, 1u << new_hashbits, + sizeof(nghttp3_map_bucket)), + .mem = map->mem, + .seed = map->seed, + .hashbits = new_hashbits, + }; (void)rv; - new_table = nghttp3_mem_calloc(map->mem, 1u << new_hashbits, - sizeof(nghttp3_map_bucket)); - if (new_table == NULL) { + if (new_map.table == NULL) { return NGHTTP3_ERR_NOMEM; } @@ -169,15 +184,15 @@ static int map_resize(nghttp3_map *map, size_t new_hashbits) { continue; } - rv = insert(new_table, new_hashbits, bkt->key, bkt->data); + rv = map_insert(&new_map, bkt->key, bkt->data); assert(0 == rv); } } nghttp3_mem_free(map->mem, map->table); + map->table = new_map.table; map->hashbits = new_hashbits; - map->table = new_table; return 0; } @@ -187,30 +202,28 @@ int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) { assert(data); - /* Load factor is 0.75 */ + /* Load factor is 7/8 */ /* Under the very initial condition, that is map->size == 0 and - map->hashbits == 0, 4 > 3 still holds nicely. */ - if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) { + map->hashbits == 0, 8 > 7 still holds nicely. */ + if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) { if (map->hashbits) { rv = map_resize(map, map->hashbits + 1); if (rv != 0) { return rv; } } else { - rv = map_resize(map, NGHTTP3_INITIAL_TABLE_LENBITS); + rv = map_resize(map, NGHTTP3_INITIAL_HASHBITS); if (rv != 0) { return rv; } } } - rv = insert(map->table, map->hashbits, key, data); + rv = map_insert(map, key, data); if (rv != 0) { return rv; } - ++map->size; - return 0; } @@ -224,7 +237,7 @@ void *nghttp3_map_find(const nghttp3_map *map, nghttp3_map_key_type key) { return NULL; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { @@ -253,7 +266,7 @@ int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h index 2b1a6ecab5cf4c..7496b486e17885 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h @@ -48,6 +48,7 @@ typedef struct nghttp3_map_bucket { typedef struct nghttp3_map { nghttp3_map_bucket *table; const nghttp3_mem *mem; + uint64_t seed; size_t size; size_t hashbits; } nghttp3_map; @@ -55,7 +56,7 @@ typedef struct nghttp3_map { /* * nghttp3_map_init initializes the map |map|. */ -void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); +void nghttp3_map_init(nghttp3_map *map, uint64_t seed, const nghttp3_mem *mem); /* * nghttp3_map_free deallocates any resources allocated for |map|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c index feefcd6fc717c3..e35bcac4e43b8f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c @@ -164,20 +164,4 @@ int nghttp3_pq_empty(const nghttp3_pq *pq) { return pq->length == 0; } size_t nghttp3_pq_size(const nghttp3_pq *pq) { return pq->length; } -int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg) { - size_t i; - - if (pq->length == 0) { - return 0; - } - - for (i = 0; i < pq->length; ++i) { - if ((*fun)(pq->q[i], arg)) { - return 1; - } - } - - return 0; -} - void nghttp3_pq_clear(nghttp3_pq *pq) { pq->length = 0; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h index 3813b529473075..c4e47b16708b53 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h @@ -112,17 +112,6 @@ int nghttp3_pq_empty(const nghttp3_pq *pq); */ size_t nghttp3_pq_size(const nghttp3_pq *pq); -typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); - -/* - * nghttp3_pq_each applies |fun| to each item in |pq|. The |arg| is - * passed as arg parameter to callback function. This function must - * not change the ordering key. If the return value from callback is - * nonzero, this function returns 1 immediately without iterating - * remaining items. Otherwise this function returns 0. - */ -int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg); - /* * nghttp3_pq_remove removes |item| from |pq|. |pq| must contain * |item| otherwise the behavior is undefined. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c index a1c7e7487c1a34..d2bac4830042ef 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c @@ -41,7 +41,12 @@ #define NGHTTP3_QPACK_MAX_QPACK_STREAMS 2000 /* Make scalar initialization form of nghttp3_qpack_static_entry */ -#define MAKE_STATIC_ENT(I, T, H) {I, T, H} +#define MAKE_STATIC_ENT(I, T, H) \ + { \ + .absidx = I, \ + .token = T, \ + .hash = H, \ + } /* Generated by mkstatichdtbl.py */ static nghttp3_qpack_static_entry token_stable[] = { @@ -166,9 +171,19 @@ static nghttp3_qpack_static_entry token_stable[] = { /* Make scalar initialization form of nghttp3_qpack_static_entry */ #define MAKE_STATIC_HD(N, V, T) \ { \ - {NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ - T, \ + .name = \ + { \ + .base = (uint8_t *)(N), \ + .len = sizeof((N)) - 1, \ + .ref = -1, \ + }, \ + .value = \ + { \ + .base = (uint8_t *)(V), \ + .len = sizeof((V)) - 1, \ + .ref = -1, \ + }, \ + .token = T, \ } static nghttp3_qpack_static_header stable[] = { @@ -828,29 +843,12 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, * ctx->max_dtable_capacity and it is initialized to 0. * |hard_max_dtable_capacity| is the upper bound of * ctx->max_dtable_capacity. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -static int qpack_context_init(nghttp3_qpack_context *ctx, - size_t hard_max_dtable_capacity, - size_t max_blocked_streams, - const nghttp3_mem *mem) { - int rv; - size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD; - size_t len2; - - for (len2 = 1; len2 < len; len2 <<= 1) - ; - - rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *), - mem); - if (rv != 0) { - return rv; - } +static void qpack_context_init(nghttp3_qpack_context *ctx, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, + const nghttp3_mem *mem) { + nghttp3_ringbuf_init(&ctx->dtable, 0, sizeof(nghttp3_qpack_entry *), mem); ctx->mem = mem; ctx->dtable_size = 0; @@ -860,8 +858,6 @@ static int qpack_context_init(nghttp3_qpack_context *ctx, ctx->max_blocked_streams = max_blocked_streams; ctx->next_absidx = 0; ctx->bad = 0; - - return 0; } static void qpack_context_free(nghttp3_qpack_context *ctx) { @@ -898,19 +894,17 @@ static int max_cnt_greater(const nghttp3_ksl_key *lhs, return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id); } -int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t hard_max_dtable_capacity, - const nghttp3_mem *mem) { - int rv; +nghttp3_ksl_search_def(max_cnt_greater, max_cnt_greater) - rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem); - if (rv != 0) { - return rv; - } +void nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t hard_max_dtable_capacity, uint64_t seed, + const nghttp3_mem *mem) { + qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem); - nghttp3_map_init(&encoder->streams, mem); + nghttp3_map_init(&encoder->streams, seed, mem); nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, + ksl_max_cnt_greater_search, sizeof(nghttp3_blocked_streams_key), mem); qpack_map_init(&encoder->dtable_map); @@ -925,8 +919,6 @@ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE; nghttp3_qpack_read_state_reset(&encoder->rstate); - - return 0; } static int map_stream_free(void *data, void *ptr) { @@ -1149,6 +1141,9 @@ int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, int blocked_stream; nghttp3_qpack_stream *stream; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (encoder->ctx.bad) { return NGHTTP3_ERR_QPACK_FATAL; } @@ -1444,7 +1439,14 @@ int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, uint32_t hash = 0; int32_t token; nghttp3_qpack_indexing_mode indexing_mode; - nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1}; + nghttp3_qpack_lookup_result sres = { + .index = -1, + .pb_index = -1, + }; + nghttp3_qpack_lookup_result dres = { + .index = -1, + .pb_index = -1, + }; nghttp3_qpack_entry *new_ent = NULL; int static_entry; int just_index = 0; @@ -1608,8 +1610,10 @@ int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, nghttp3_qpack_lookup_result nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, nghttp3_qpack_indexing_mode indexing_mode) { - nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx, - 0, -1}; + nghttp3_qpack_lookup_result res = { + .index = (nghttp3_ssize)token_stable[token].absidx, + .pb_index = -1, + }; nghttp3_qpack_static_entry *ent; nghttp3_qpack_static_header *hdr; size_t i; @@ -1639,7 +1643,10 @@ nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt, int allow_blocking) { - nghttp3_qpack_lookup_result res = {-1, 0, -1}; + nghttp3_qpack_lookup_result res = { + .index = -1, + .pb_index = -1, + }; int exact_match = 0; nghttp3_qpack_entry *match, *pb_match; @@ -1694,7 +1701,6 @@ static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx, int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, const nghttp3_mem *mem) { - int rv; nghttp3_qpack_stream *stream; stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream)); @@ -1702,12 +1708,8 @@ int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_ringbuf_init(&stream->refs, 4, - sizeof(nghttp3_qpack_header_block_ref *), mem); - if (rv != 0) { - nghttp3_mem_free(mem, stream); - return rv; - } + nghttp3_ringbuf_init(&stream->refs, 0, + sizeof(nghttp3_qpack_header_block_ref *), mem); nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); @@ -1759,17 +1761,23 @@ int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, int rv; if (nghttp3_ringbuf_full(&stream->refs)) { - rv = nghttp3_ringbuf_reserve(&stream->refs, - nghttp3_ringbuf_len(&stream->refs) * 2); + rv = nghttp3_ringbuf_reserve( + &stream->refs, + nghttp3_max_size(4, nghttp3_ringbuf_len(&stream->refs) * 2)); if (rv != 0) { return rv; } } + rv = nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe); + if (rv != 0) { + return rv; + } + dest = nghttp3_ringbuf_push_back(&stream->refs); *dest = ref; - return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe); + return 0; } void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) { @@ -1834,7 +1842,7 @@ static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder, int h = 0; hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); - if (hlen * 4 < nv->valuelen * 3) { + if (hlen < nv->valuelen) { h = 1; len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen; } else { @@ -1925,7 +1933,7 @@ static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, int nh = 0, vh = 0; nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen); - if (nhlen * 4 < nv->namelen * 3) { + if (nhlen < nv->namelen) { nh = 1; len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen; } else { @@ -1933,7 +1941,7 @@ static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, } vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); - if (vhlen * 4 < nv->valuelen * 3) { + if (vhlen < nv->valuelen) { vh = 1; len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen; } else { @@ -2083,8 +2091,9 @@ int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, hash); if (nghttp3_ringbuf_full(&ctx->dtable)) { - rv = nghttp3_ringbuf_reserve(&ctx->dtable, - nghttp3_ringbuf_len(&ctx->dtable) * 2); + rv = nghttp3_ringbuf_reserve( + &ctx->dtable, + nghttp3_max_size(128, nghttp3_ringbuf_len(&ctx->dtable) * 2)); if (rv != 0) { goto fail; } @@ -2259,10 +2268,11 @@ void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) { int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream *stream) { nghttp3_blocked_streams_key bsk = { - nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), - nghttp3_qpack_header_block_ref, max_cnts_pe) - ->max_cnt, - (uint64_t)stream->stream_id}; + .max_cnt = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + .id = (uint64_t)stream->stream_id, + }; return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream); } @@ -2270,10 +2280,11 @@ int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream *stream) { nghttp3_blocked_streams_key bsk = { - nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), - nghttp3_qpack_header_block_ref, max_cnts_pe) - ->max_cnt, - (uint64_t)stream->stream_id}; + .max_cnt = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + .id = (uint64_t)stream->stream_id, + }; nghttp3_ksl_it it; /* This is purely debugging purpose only */ @@ -2287,7 +2298,9 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, uint64_t max_cnt) { - nghttp3_blocked_streams_key bsk = {max_cnt, 0}; + nghttp3_blocked_streams_key bsk = { + .max_cnt = max_cnt, + }; nghttp3_ksl_it it; it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); @@ -2667,17 +2680,12 @@ void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) { rstate->huffman_encoded = 0; } -int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t hard_max_dtable_capacity, - size_t max_blocked_streams, - const nghttp3_mem *mem) { - int rv; - - rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity, - max_blocked_streams, mem); - if (rv != 0) { - return rv; - } +void nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, + const nghttp3_mem *mem) { + qpack_context_init(&decoder->ctx, hard_max_dtable_capacity, + max_blocked_streams, mem); decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; decoder->opcode = 0; @@ -2687,8 +2695,6 @@ int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, nghttp3_qpack_read_state_reset(&decoder->rstate); nghttp3_buf_init(&decoder->dbuf); - - return 0; } void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) { @@ -3171,6 +3177,7 @@ int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); nghttp3_rcbuf_decref(qnv.value); + decoder->rstate.value = NULL; return rv; } @@ -3197,6 +3204,7 @@ int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) { rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); nghttp3_rcbuf_decref(qnv.value); + decoder->rstate.value = NULL; nghttp3_rcbuf_decref(qnv.name); return rv; @@ -3250,7 +3258,9 @@ int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) { rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); nghttp3_rcbuf_decref(qnv.value); + decoder->rstate.value = NULL; nghttp3_rcbuf_decref(qnv.name); + decoder->rstate.name = NULL; return rv; } @@ -3818,6 +3828,9 @@ int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, uint8_t *p; int rv; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (qpack_decoder_dbuf_overflow(decoder)) { return NGHTTP3_ERR_QPACK_FATAL; } @@ -4059,10 +4072,9 @@ void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, sctx->rstate.value = NULL; } -int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t hard_max_dtable_capacity, - const nghttp3_mem *mem) { - int rv; +int nghttp3_qpack_encoder_new2(nghttp3_qpack_encoder **pencoder, + size_t hard_max_dtable_capacity, uint64_t seed, + const nghttp3_mem *mem) { nghttp3_qpack_encoder *p; p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder)); @@ -4070,16 +4082,19 @@ int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem); - if (rv != 0) { - return rv; - } + nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, seed, mem); *pencoder = p; return 0; } +int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, + size_t hard_max_dtable_capacity, + const nghttp3_mem *mem) { + return nghttp3_qpack_encoder_new2(pencoder, hard_max_dtable_capacity, 0, mem); +} + void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) { const nghttp3_mem *mem; @@ -4098,6 +4113,9 @@ int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, const nghttp3_mem *mem) { nghttp3_qpack_stream_context *p; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context)); if (p == NULL) { return NGHTTP3_ERR_NOMEM; @@ -4127,7 +4145,6 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, size_t hard_max_dtable_capacity, size_t max_blocked_streams, const nghttp3_mem *mem) { - int rv; nghttp3_qpack_decoder *p; p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder)); @@ -4135,11 +4152,8 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, - max_blocked_streams, mem); - if (rv != 0) { - return rv; - } + nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, max_blocked_streams, + mem); *pdecoder = p; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h index d2bb8a3581135b..77d264a533fba4 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h @@ -269,17 +269,12 @@ struct nghttp3_qpack_encoder { /* * nghttp3_qpack_encoder_init initializes |encoder|. * |hard_max_dtable_capacity| is the upper bound of the dynamic table - * capacity. |mem| is a memory allocator. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. + * capacity. |seed| is used to initialize nghttp3_map. |mem| is a + * memory allocator. */ -int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t hard_max_dtable_capacity, - const nghttp3_mem *mem); +void nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t hard_max_dtable_capacity, uint64_t seed, + const nghttp3_mem *mem); /* * nghttp3_qpack_encoder_free frees memory allocated for |encoder|. @@ -800,17 +795,11 @@ struct nghttp3_qpack_decoder { * |hard_max_dtable_capacity| is the upper bound of the dynamic table * capacity. |max_blocked_streams| is the maximum number of stream * which can be blocked. |mem| is a memory allocator. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t hard_max_dtable_capacity, - size_t max_blocked_streams, - const nghttp3_mem *mem); +void nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, + const nghttp3_mem *mem); /* * nghttp3_qpack_decoder_free frees memory allocated for |decoder|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c index 3398f3f5080e60..dbc9a764e7bf26 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c @@ -88,7 +88,9 @@ nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, int fin) { uint8_t *p = dest; const uint8_t *end = src + srclen; - nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0}; + nghttp3_qpack_huffman_decode_node node = { + .fstate = ctx->fstate, + }; const nghttp3_qpack_huffman_decode_node *t = &node; uint8_t c; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_range.c b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c index af810a2c5929db..b5f95bb83fc852 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_range.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c @@ -33,7 +33,7 @@ void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) { nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, const nghttp3_range *b) { - nghttp3_range r = {0, 0}; + nghttp3_range r = {0}; uint64_t begin = nghttp3_max_uint64(a->begin, b->begin); uint64_t end = nghttp3_min_uint64(a->end, b->end); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c index 7d3ab39bf82a7f..ccd1d3e99ab55e 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c @@ -33,18 +33,21 @@ #include "nghttp3_macro.h" +#ifndef NDEBUG static int ispow2(size_t n) { -#if defined(_MSC_VER) && !defined(__clang__) && \ - (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941)) +# if defined(DISABLE_POPCNT) || \ + (defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941))) return n && !(n & (n - 1)); -#elif defined(WIN32) +# elif defined(WIN32) return 1 == __popcnt((unsigned int)n); -#else /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ - (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +# else /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ + (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ return 1 == __builtin_popcount((unsigned int)n); -#endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ - (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +# endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ + (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ } +#endif /* !defined(NDEBUG) */ int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, const nghttp3_mem *mem) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h index b154290a51d5a5..691c40b93d61e0 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h @@ -103,7 +103,9 @@ void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len); void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset); /* nghttp3_ringbuf_len returns the number of elements stored. */ -#define nghttp3_ringbuf_len(RB) ((RB)->len) +static inline size_t nghttp3_ringbuf_len(const nghttp3_ringbuf *rb) { + return rb->len; +} /* nghttp3_ringbuf_full returns nonzero if |rb| is full. */ int nghttp3_ringbuf_full(nghttp3_ringbuf *rb); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_settings.c b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.c new file mode 100644 index 00000000000000..2b7cd2892b3cc9 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.c @@ -0,0 +1,95 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_settings.h" + +#include +#include + +#include "nghttp3_conv.h" +#include "nghttp3_unreachable.h" + +/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the + dynamic table capacity that QPACK encoder is willing to use. */ +#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096 + +void nghttp3_settings_default_versioned(int settings_version, + nghttp3_settings *settings) { + size_t len = nghttp3_settingslen_version(settings_version); + + memset(settings, 0, len); + + switch (settings_version) { + case NGHTTP3_SETTINGS_VERSION: + case NGHTTP3_SETTINGS_V1: + settings->max_field_section_size = NGHTTP3_VARINT_MAX; + settings->qpack_encoder_max_dtable_capacity = + NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY; + + break; + } +} + +static void settings_copy(nghttp3_settings *dest, const nghttp3_settings *src, + int settings_version) { + assert(settings_version != NGHTTP3_SETTINGS_VERSION); + + memcpy(dest, src, nghttp3_settingslen_version(settings_version)); +} + +const nghttp3_settings * +nghttp3_settings_convert_to_latest(nghttp3_settings *dest, int settings_version, + const nghttp3_settings *src) { + if (settings_version == NGHTTP3_SETTINGS_VERSION) { + return src; + } + + nghttp3_settings_default(dest); + + settings_copy(dest, src, settings_version); + + return dest; +} + +void nghttp3_settings_convert_to_old(int settings_version, + nghttp3_settings *dest, + const nghttp3_settings *src) { + assert(settings_version != NGHTTP3_SETTINGS_VERSION); + + settings_copy(dest, src, settings_version); +} + +size_t nghttp3_settingslen_version(int settings_version) { + nghttp3_settings settings; + + switch (settings_version) { + case NGHTTP3_SETTINGS_VERSION: + return sizeof(settings); + case NGHTTP3_SETTINGS_V1: + return offsetof(nghttp3_settings, h3_datagram) + + sizeof(settings.h3_datagram); + default: + nghttp3_unreachable(); + } +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_settings.h b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.h new file mode 100644 index 00000000000000..0632aafc489ee5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.h @@ -0,0 +1,74 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_SETTINGS_H +#define NGHTTP3_SETTINGS_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +/* + * nghttp3_settings_convert_to_latest converts |src| of version + * |settings_version| to the latest version NGHTTP3_SETTINGS_VERSION. + * + * |dest| must point to the latest version. |src| may be the older + * version, and if so, it may have fewer fields. Accessing those + * fields causes undefined behavior. + * + * If |settings_version| == NGHTTP3_SETTINGS_VERSION, no conversion is + * made, and |src| is returned. Otherwise, first |dest| is + * initialized via nghttp3_settings_default, and then all valid fields + * in |src| are copied into |dest|. Finally, |dest| is returned. + */ +const nghttp3_settings * +nghttp3_settings_convert_to_latest(nghttp3_settings *dest, int settings_version, + const nghttp3_settings *src); + +/* + * nghttp3_settings_convert_to_old converts |src| of the latest + * version to |dest| of version |settings_version|. + * + * |settings_version| must not be the latest version + * NGHTTP3_SETTINGS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version |settings_version| + * from |src| to |dest|. + */ +void nghttp3_settings_convert_to_old(int settings_version, + nghttp3_settings *dest, + const nghttp3_settings *src); + +/* + * nghttp3_settingslen_version returns the effective length of + * nghttp3_settings at the version |settings_version|. + */ +size_t nghttp3_settingslen_version(int settings_version); + +#endif /* !defined(NGHTTP3_SETTINGS_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c index 328cddd488fd6f..d6f8fac3c528bb 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c @@ -44,7 +44,7 @@ /* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */ #define NGHTTP3_MIN_RBLEN 4 -nghttp3_objalloc_def(stream, nghttp3_stream, oplent); +nghttp3_objalloc_def(stream, nghttp3_stream, oplent) int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, const nghttp3_stream_callbacks *callbacks, @@ -57,10 +57,19 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, return NGHTTP3_ERR_NOMEM; } - memset(stream, 0, sizeof(*stream)); - - stream->out_chunk_objalloc = out_chunk_objalloc; - stream->stream_objalloc = stream_objalloc; + *stream = (nghttp3_stream){ + .out_chunk_objalloc = out_chunk_objalloc, + .stream_objalloc = stream_objalloc, + .qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX, + .mem = mem, + .rx = + { + .http.status_code = -1, + .http.content_length = -1, + .http.pri.urgency = NGHTTP3_DEFAULT_URGENCY, + }, + .error_code = NGHTTP3_H3_NO_ERROR, + }; nghttp3_tnode_init(&stream->node, stream_id); @@ -71,13 +80,6 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); - stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; - stream->mem = mem; - stream->rx.http.status_code = -1; - stream->rx.http.content_length = -1; - stream->rx.http.pri.urgency = NGHTTP3_DEFAULT_URGENCY; - stream->error_code = NGHTTP3_H3_NO_ERROR; - if (callbacks) { stream->callbacks = *callbacks; } @@ -140,7 +142,7 @@ static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { for (i = 0; i < len; ++i) { frent = nghttp3_ringbuf_get(frq, i); - switch (frent->fr.hd.type) { + switch (frent->fr.type) { case NGHTTP3_FRAME_HEADERS: nghttp3_frame_headers_free(&frent->fr.headers, mem); break; @@ -181,42 +183,45 @@ void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) { nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, const uint8_t *begin, const uint8_t *end, int fin) { - const uint8_t *orig_begin = begin; - size_t len; + size_t len, vlen; + uint8_t *p; assert(begin != end); if (rvint->left == 0) { assert(rvint->acc == 0); - len = nghttp3_get_varintlen(begin); - if (len <= (size_t)(end - begin)) { + vlen = nghttp3_get_varintlen(begin); + len = nghttp3_min_size(vlen, (size_t)(end - begin)); + if (vlen <= len) { nghttp3_get_varint(&rvint->acc, begin); - return (nghttp3_ssize)len; + return (nghttp3_ssize)vlen; } if (fin) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - rvint->acc = nghttp3_get_varint_fb(begin++); - rvint->left = len - 1; - } - - len = nghttp3_min_size(rvint->left, (size_t)(end - begin)); - end = begin + len; + p = (uint8_t *)&rvint->acc + (sizeof(rvint->acc) - vlen); + memcpy(p, begin, len); + *p &= 0x3f; + rvint->left = vlen - len; - for (; begin != end;) { - rvint->acc = (rvint->acc << 8) + *begin++; + return (nghttp3_ssize)len; } + len = nghttp3_min_size(rvint->left, (size_t)(end - begin)); + p = (uint8_t *)&rvint->acc + (sizeof(rvint->acc) - rvint->left); + memcpy(p, begin, len); rvint->left -= len; - if (fin && rvint->left) { + if (rvint->left == 0) { + rvint->acc = (int64_t)nghttp3_ntohl64((uint64_t)rvint->acc); + } else if (fin) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - return (nghttp3_ssize)(begin - orig_begin); + return (nghttp3_ssize)len; } int nghttp3_stream_frq_add(nghttp3_stream *stream, @@ -250,7 +255,7 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) { frent = nghttp3_ringbuf_get(frq, 0); - switch (frent->fr.hd.type) { + switch (frent->fr.type) { case NGHTTP3_FRAME_SETTINGS: rv = nghttp3_stream_write_settings(stream, frent); if (rv != 0) { @@ -290,6 +295,13 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { nghttp3_frame_priority_update_free(&frent->fr.priority_update, stream->mem); break; + case NGHTTP3_FRAME_ORIGIN: + rv = nghttp3_stream_write_origin(stream, frent); + if (rv != 0) { + return rv; + } + + break; default: /* TODO Not implemented */ break; @@ -301,12 +313,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { return 0; } -static void typed_buf_shared_init(nghttp3_typed_buf *tbuf, - const nghttp3_buf *chunk) { - nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED); - tbuf->buf.pos = tbuf->buf.last; -} - int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { size_t len = nghttp3_put_varintlen((int64_t)stream->type); nghttp3_buf *chunk; @@ -319,7 +325,7 @@ int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); tbuf.buf.last = chunk->last; @@ -336,12 +342,17 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, struct { nghttp3_frame_settings settings; nghttp3_settings_entry iv[15]; - } fr; + } fr = { + .settings = + { + .type = NGHTTP3_FRAME_SETTINGS, + .niv = 3, + }, + }; nghttp3_settings_entry *iv; nghttp3_settings *local_settings = frent->aux.settings.local_settings; + int64_t payloadlen; - fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS; - fr.settings.niv = 3; iv = &fr.settings.iv[0]; iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE; @@ -365,7 +376,7 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, ++fr.settings.niv; } - len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings); + len = nghttp3_frame_write_settings_len(&payloadlen, &fr.settings); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -373,9 +384,10 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings); + chunk->last = + nghttp3_frame_write_settings(chunk->last, &fr.settings, payloadlen); tbuf.buf.last = chunk->last; @@ -389,8 +401,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream, int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; + int64_t payloadlen; - len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr); + len = nghttp3_frame_write_goaway_len(&payloadlen, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -398,9 +411,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_goaway(chunk->last, fr); + chunk->last = nghttp3_frame_write_goaway(chunk->last, fr, payloadlen); tbuf.buf.last = chunk->last; @@ -414,8 +427,9 @@ int nghttp3_stream_write_priority_update(nghttp3_stream *stream, int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; + int64_t payloadlen; - len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr); + len = nghttp3_frame_write_priority_update_len(&payloadlen, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -423,12 +437,52 @@ int nghttp3_stream_write_priority_update(nghttp3_stream *stream, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); + + chunk->last = + nghttp3_frame_write_priority_update(chunk->last, fr, payloadlen); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_origin(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_origin *fr = &frent->fr.origin; + nghttp3_buf *chunk; + nghttp3_buf buf; + nghttp3_typed_buf tbuf; + int rv; + + rv = nghttp3_stream_ensure_chunk( + stream, nghttp3_frame_write_hd_len(fr->type, (int64_t)fr->origin_list.len)); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr); + chunk->last = + nghttp3_frame_write_hd(chunk->last, fr->type, (int64_t)fr->origin_list.len); tbuf.buf.last = chunk->last; + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + return rv; + } + + if (fr->origin_list.len == 0) { + return 0; + } + + nghttp3_buf_wrap_init(&buf, (uint8_t *)fr->origin_list.base, + fr->origin_list.len); + buf.last = buf.end; + nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN_NO_ACK); + return nghttp3_stream_outq_add(stream, &tbuf); } @@ -455,26 +509,25 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, size_t len; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; - nghttp3_frame_hd hd; uint8_t raw_pbuf[16]; size_t pbuflen, rbuflen, ebuflen; + int64_t payloadlen; nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf)); rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, stream->node.id, nva, nvlen); if (rv != 0) { - goto fail; + return rv; } pbuflen = nghttp3_buf_len(&pbuf); rbuflen = nghttp3_buf_len(rbuf); ebuflen = nghttp3_buf_len(ebuf); - hd.type = frame_type; - hd.length = (int64_t)(pbuflen + rbuflen); + payloadlen = (int64_t)(pbuflen + rbuflen); - len = nghttp3_frame_write_hd_len(&hd) + pbuflen; + len = nghttp3_frame_write_hd_len(frame_type, payloadlen) + pbuflen; if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) { len += rbuflen; @@ -482,13 +535,13 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { - goto fail; + return rv; } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + chunk->last = nghttp3_frame_write_hd(chunk->last, frame_type, payloadlen); chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen); nghttp3_buf_init(&pbuf); @@ -498,13 +551,13 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_outq_add(stream, &tbuf); if (rv != 0) { - goto fail; + return rv; } nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE); rv = nghttp3_stream_outq_add(stream, &tbuf); if (rv != 0) { - goto fail; + return rv; } nghttp3_buf_init(rbuf); } else if (rbuflen) { @@ -513,7 +566,7 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_outq_add(stream, &tbuf); if (rv != 0) { - goto fail; + return rv; } nghttp3_buf_reset(rbuf); } @@ -532,18 +585,18 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen); if (rv != 0) { - goto fail; + return rv; } chunk = nghttp3_stream_get_chunk(qenc_stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); chunk->last = nghttp3_cpymem(chunk->last, ebuf->pos, ebuflen); tbuf.buf.last = chunk->last; rv = nghttp3_stream_outq_add(qenc_stream, &tbuf); if (rv != 0) { - goto fail; + return rv; } nghttp3_buf_reset(ebuf); } @@ -553,10 +606,6 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, assert(0 == nghttp3_buf_len(ebuf)); return 0; - -fail: - - return rv; } int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, @@ -570,7 +619,6 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, nghttp3_conn *conn = stream->conn; int64_t datalen; uint32_t flags = 0; - nghttp3_frame_hd hd; nghttp3_vec vec[8]; nghttp3_vec *v; nghttp3_ssize sveccnt; @@ -624,10 +672,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, } } - hd.type = NGHTTP3_FRAME_DATA; - hd.length = datalen; - - len = nghttp3_frame_write_hd_len(&hd); + len = nghttp3_frame_write_hd_len(NGHTTP3_FRAME_DATA, datalen); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -635,9 +680,10 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + chunk->last = + nghttp3_frame_write_hd(chunk->last, NGHTTP3_FRAME_DATA, datalen); tbuf.buf.last = chunk->last; @@ -690,7 +736,7 @@ int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) { } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); nghttp3_qpack_decoder_write_decoder(qdec, chunk); @@ -717,17 +763,16 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream, if (len) { dest = nghttp3_ringbuf_get(outq, len - 1); if (dest->type == tbuf->type && dest->type == NGHTTP3_BUF_TYPE_SHARED && - dest->buf.begin == tbuf->buf.begin && dest->buf.last == tbuf->buf.pos) { + dest->buf.end == tbuf->buf.end && dest->buf.last == tbuf->buf.pos) { /* If we have already written last entry, adjust outq_idx and offset so that this entry is eligible to send. */ if (len == stream->outq_idx) { --stream->outq_idx; - stream->outq_offset = nghttp3_buf_len(&dest->buf); } dest->buf.last = tbuf->buf.last; - /* TODO Is this required? */ - dest->buf.end = tbuf->buf.end; + + assert(dest->buf.end == tbuf->buf.end); return 0; } @@ -817,34 +862,24 @@ size_t nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, nghttp3_ringbuf *outq = &stream->outq; size_t len = nghttp3_ringbuf_len(outq); size_t i = stream->outq_idx; - uint64_t offset = stream->outq_offset; size_t buflen; nghttp3_vec *vbegin = vec, *vend = vec + veccnt; nghttp3_typed_buf *tbuf; assert(veccnt > 0); - if (i < len) { + for (; i < len && vec != vend; ++i) { tbuf = nghttp3_ringbuf_get(outq, i); buflen = nghttp3_buf_len(&tbuf->buf); - if (offset < buflen) { - vec->base = tbuf->buf.pos + offset; - vec->len = (size_t)(buflen - offset); - ++vec; - } else { - /* This is the only case that satisfies offset >= buflen */ - assert(0 == offset); - assert(0 == buflen); + if (buflen == 0) { + continue; } - ++i; + vec->base = tbuf->buf.pos; + vec->len = buflen; - for (; i < len && vec != vend; ++i, ++vec) { - tbuf = nghttp3_ringbuf_get(outq, i); - vec->base = tbuf->buf.pos; - vec->len = nghttp3_buf_len(&tbuf->buf); - } + ++vec; } /* TODO Rework this if we have finished implementing HTTP @@ -859,26 +894,27 @@ void nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) { nghttp3_ringbuf *outq = &stream->outq; size_t i; size_t len = nghttp3_ringbuf_len(outq); - uint64_t offset = stream->outq_offset + n; size_t buflen; nghttp3_typed_buf *tbuf; + stream->unsent_bytes -= n; + for (i = stream->outq_idx; i < len; ++i) { tbuf = nghttp3_ringbuf_get(outq, i); buflen = nghttp3_buf_len(&tbuf->buf); - if (offset >= buflen) { - offset -= buflen; - continue; + if (n < buflen) { + tbuf->buf.pos += n; + + break; } - break; + tbuf->buf.pos = tbuf->buf.last; + n -= buflen; } - assert(i < len || offset == 0); + assert(i < len || n == 0); - stream->unsent_bytes -= n; stream->outq_idx = i; - stream->outq_offset = offset; } int nghttp3_stream_outq_write_done(nghttp3_stream *stream) { @@ -898,13 +934,13 @@ static void stream_pop_outq_entry(nghttp3_stream *stream, nghttp3_buf_free(&tbuf->buf, stream->mem); break; case NGHTTP3_BUF_TYPE_ALIEN: + case NGHTTP3_BUF_TYPE_ALIEN_NO_ACK: break; case NGHTTP3_BUF_TYPE_SHARED: assert(nghttp3_ringbuf_len(chunks)); chunk = nghttp3_ringbuf_get(chunks, 0); - assert(chunk->begin == tbuf->buf.begin); assert(chunk->end == tbuf->buf.end); if (chunk->last == tbuf->buf.last) { @@ -927,14 +963,13 @@ static void stream_pop_outq_entry(nghttp3_stream *stream, int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) { nghttp3_ringbuf *outq = &stream->outq; size_t buflen; - size_t npopped = 0; uint64_t nack; nghttp3_typed_buf *tbuf; int rv; for (; nghttp3_ringbuf_len(outq);) { tbuf = nghttp3_ringbuf_get(outq, 0); - buflen = nghttp3_buf_len(&tbuf->buf); + buflen = (size_t)(tbuf->buf.last - tbuf->buf.begin); /* For NGHTTP3_BUF_TYPE_ALIEN, we never add 0 length buffer. */ if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN && stream->ack_offset < offset && @@ -949,17 +984,13 @@ int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) { } } - if (offset >= stream->ack_base + buflen) { + if (stream->outq_idx > 0 && offset >= stream->ack_base + buflen) { stream_pop_outq_entry(stream, tbuf); stream->ack_base += buflen; stream->ack_offset = stream->ack_base; - ++npopped; - if (stream->outq_idx + 1 == npopped) { - stream->outq_offset = 0; - break; - } + --stream->outq_idx; continue; } @@ -967,13 +998,6 @@ int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) { break; } - assert(stream->outq_idx + 1 >= npopped); - if (stream->outq_idx >= npopped) { - stream->outq_idx -= npopped; - } else { - stream->outq_idx = 0; - } - stream->ack_offset = offset; return 0; @@ -1045,19 +1069,17 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, switch (stream->rx.hstate) { case NGHTTP3_HTTP_STATE_NONE: - return NGHTTP3_ERR_H3_INTERNAL_ERROR; + nghttp3_unreachable(); case NGHTTP3_HTTP_STATE_REQ_INITIAL: - switch (event) { - case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN; - return 0; - default: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN; + + return 0; case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END; return 0; case NGHTTP3_HTTP_STATE_REQ_HEADERS_END: @@ -1080,12 +1102,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_DATA_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_DATA_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END; return 0; case NGHTTP3_HTTP_STATE_REQ_DATA_END: @@ -1108,12 +1128,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END; return 0; case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END: @@ -1129,7 +1147,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; case NGHTTP3_HTTP_STATE_REQ_END: - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; case NGHTTP3_HTTP_STATE_RESP_INITIAL: if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; @@ -1137,9 +1155,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN; return 0; case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END; return 0; case NGHTTP3_HTTP_STATE_RESP_HEADERS_END: @@ -1169,12 +1185,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_DATA_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_DATA_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END; return 0; case NGHTTP3_HTTP_STATE_RESP_DATA_END: @@ -1197,17 +1211,15 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END; return 0; case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END: if (event != NGHTTP3_HTTP_EVENT_MSG_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } rv = nghttp3_http_on_remote_end_stream(stream); if (rv != 0) { @@ -1216,7 +1228,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; case NGHTTP3_HTTP_STATE_RESP_END: - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; default: nghttp3_unreachable(); } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h index 7d296febf9135f..759cf687a0c108 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h @@ -69,6 +69,8 @@ typedef enum nghttp3_ctrl_stream_state { NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE, NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID, NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE, + NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN, + NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN, } nghttp3_ctrl_stream_state; typedef enum nghttp3_req_stream_state { @@ -129,10 +131,6 @@ typedef struct nghttp3_stream_read_state { /* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server received PRIORITY_UPDATE frame for this stream. */ #define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u -/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that - NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while - processing incoming HTTP fields. */ -#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u typedef enum nghttp3_stream_http_state { NGHTTP3_HTTP_STATE_NONE, @@ -223,9 +221,6 @@ struct nghttp3_stream { uint64_t unsent_bytes; /* outq_idx is an index into outq where next write is made. */ size_t outq_idx; - /* outq_offset is write offset relative to the element at outq_idx - in outq. */ - uint64_t outq_offset; /* ack_base is the number of bytes acknowledged by a remote endpoint where the first element in outq is positioned at. */ uint64_t ack_base; @@ -255,7 +250,7 @@ struct nghttp3_stream { }; }; -nghttp3_objalloc_decl(stream, nghttp3_stream, oplent); +nghttp3_objalloc_decl(stream, nghttp3_stream, oplent) typedef struct nghttp3_frame_entry { nghttp3_frame fr; @@ -322,6 +317,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream, int nghttp3_stream_write_priority_update(nghttp3_stream *stream, nghttp3_frame_entry *frent); +int nghttp3_stream_write_origin(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need); nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c index 939821d84eac3d..b88e2255b2556d 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c @@ -28,8 +28,11 @@ #include -static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, - NGHTTP3_VERSION}; +static nghttp3_info version = { + .age = NGHTTP3_VERSION_AGE, + .version_num = NGHTTP3_VERSION_NUM, + .version_str = NGHTTP3_VERSION, +}; const nghttp3_info *nghttp3_version(int least_version) { if (least_version > NGHTTP3_VERSION_NUM) { diff --git a/deps/ngtcp2/nghttp3/lib/sfparse/COPYING b/deps/ngtcp2/nghttp3/lib/sfparse/COPYING deleted file mode 100644 index 8212d82d83ab74..00000000000000 --- a/deps/ngtcp2/nghttp3/lib/sfparse/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2023 sfparse contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c b/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c deleted file mode 100644 index d0328cf40c21ea..00000000000000 --- a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c +++ /dev/null @@ -1,1517 +0,0 @@ -/* - * sfparse - * - * Copyright (c) 2023 sfparse contributors - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2015 nghttp2 contributors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "sfparse.h" - -#include -#include -#include - -#define SF_STATE_DICT 0x08u -#define SF_STATE_LIST 0x10u -#define SF_STATE_ITEM 0x18u - -#define SF_STATE_INNER_LIST 0x04u - -#define SF_STATE_BEFORE 0x00u -#define SF_STATE_BEFORE_PARAMS 0x01u -#define SF_STATE_PARAMS 0x02u -#define SF_STATE_AFTER 0x03u - -#define SF_STATE_OP_MASK 0x03u - -#define SF_SET_STATE_AFTER(NAME) (SF_STATE_##NAME | SF_STATE_AFTER) -#define SF_SET_STATE_BEFORE_PARAMS(NAME) \ - (SF_STATE_##NAME | SF_STATE_BEFORE_PARAMS) -#define SF_SET_STATE_INNER_LIST_BEFORE(NAME) \ - (SF_STATE_##NAME | SF_STATE_INNER_LIST | SF_STATE_BEFORE) - -#define SF_STATE_DICT_AFTER SF_SET_STATE_AFTER(DICT) -#define SF_STATE_DICT_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(DICT) -#define SF_STATE_DICT_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(DICT) - -#define SF_STATE_LIST_AFTER SF_SET_STATE_AFTER(LIST) -#define SF_STATE_LIST_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(LIST) -#define SF_STATE_LIST_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(LIST) - -#define SF_STATE_ITEM_AFTER SF_SET_STATE_AFTER(ITEM) -#define SF_STATE_ITEM_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(ITEM) -#define SF_STATE_ITEM_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(ITEM) - -#define SF_STATE_INITIAL 0x00u - -#define DIGIT_CASES \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9' - -#define LCALPHA_CASES \ - case 'a': \ - case 'b': \ - case 'c': \ - case 'd': \ - case 'e': \ - case 'f': \ - case 'g': \ - case 'h': \ - case 'i': \ - case 'j': \ - case 'k': \ - case 'l': \ - case 'm': \ - case 'n': \ - case 'o': \ - case 'p': \ - case 'q': \ - case 'r': \ - case 's': \ - case 't': \ - case 'u': \ - case 'v': \ - case 'w': \ - case 'x': \ - case 'y': \ - case 'z' - -#define UCALPHA_CASES \ - case 'A': \ - case 'B': \ - case 'C': \ - case 'D': \ - case 'E': \ - case 'F': \ - case 'G': \ - case 'H': \ - case 'I': \ - case 'J': \ - case 'K': \ - case 'L': \ - case 'M': \ - case 'N': \ - case 'O': \ - case 'P': \ - case 'Q': \ - case 'R': \ - case 'S': \ - case 'T': \ - case 'U': \ - case 'V': \ - case 'W': \ - case 'X': \ - case 'Y': \ - case 'Z' - -#define ALPHA_CASES \ - UCALPHA_CASES: \ - LCALPHA_CASES - -#define TOKEN_CASES \ - case '!': \ - case '#': \ - case '$': \ - case '%': \ - case '&': \ - case '\'': \ - case '*': \ - case '+': \ - case '-': \ - case '.': \ - case '/': \ - DIGIT_CASES: \ - case ':': \ - UCALPHA_CASES: \ - case '^': \ - case '_': \ - case '`': \ - LCALPHA_CASES: \ - case '|': \ - case '~' - -#define LCHEXALPHA_CASES \ - case 'a': \ - case 'b': \ - case 'c': \ - case 'd': \ - case 'e': \ - case 'f' - -#define X00_1F_CASES \ - case 0x00: \ - case 0x01: \ - case 0x02: \ - case 0x03: \ - case 0x04: \ - case 0x05: \ - case 0x06: \ - case 0x07: \ - case 0x08: \ - case 0x09: \ - case 0x0a: \ - case 0x0b: \ - case 0x0c: \ - case 0x0d: \ - case 0x0e: \ - case 0x0f: \ - case 0x10: \ - case 0x11: \ - case 0x12: \ - case 0x13: \ - case 0x14: \ - case 0x15: \ - case 0x16: \ - case 0x17: \ - case 0x18: \ - case 0x19: \ - case 0x1a: \ - case 0x1b: \ - case 0x1c: \ - case 0x1d: \ - case 0x1e: \ - case 0x1f - -#define X20_21_CASES \ - case ' ': \ - case '!' - -#define X23_5B_CASES \ - case '#': \ - case '$': \ - case '%': \ - case '&': \ - case '\'': \ - case '(': \ - case ')': \ - case '*': \ - case '+': \ - case ',': \ - case '-': \ - case '.': \ - case '/': \ - DIGIT_CASES: \ - case ':': \ - case ';': \ - case '<': \ - case '=': \ - case '>': \ - case '?': \ - case '@': \ - UCALPHA_CASES: \ - case '[' - -#define X5D_7E_CASES \ - case ']': \ - case '^': \ - case '_': \ - case '`': \ - LCALPHA_CASES: \ - case '{': \ - case '|': \ - case '}': \ - case '~' - -#define X7F_FF_CASES \ - case 0x7f: \ - case 0x80: \ - case 0x81: \ - case 0x82: \ - case 0x83: \ - case 0x84: \ - case 0x85: \ - case 0x86: \ - case 0x87: \ - case 0x88: \ - case 0x89: \ - case 0x8a: \ - case 0x8b: \ - case 0x8c: \ - case 0x8d: \ - case 0x8e: \ - case 0x8f: \ - case 0x90: \ - case 0x91: \ - case 0x92: \ - case 0x93: \ - case 0x94: \ - case 0x95: \ - case 0x96: \ - case 0x97: \ - case 0x98: \ - case 0x99: \ - case 0x9a: \ - case 0x9b: \ - case 0x9c: \ - case 0x9d: \ - case 0x9e: \ - case 0x9f: \ - case 0xa0: \ - case 0xa1: \ - case 0xa2: \ - case 0xa3: \ - case 0xa4: \ - case 0xa5: \ - case 0xa6: \ - case 0xa7: \ - case 0xa8: \ - case 0xa9: \ - case 0xaa: \ - case 0xab: \ - case 0xac: \ - case 0xad: \ - case 0xae: \ - case 0xaf: \ - case 0xb0: \ - case 0xb1: \ - case 0xb2: \ - case 0xb3: \ - case 0xb4: \ - case 0xb5: \ - case 0xb6: \ - case 0xb7: \ - case 0xb8: \ - case 0xb9: \ - case 0xba: \ - case 0xbb: \ - case 0xbc: \ - case 0xbd: \ - case 0xbe: \ - case 0xbf: \ - case 0xc0: \ - case 0xc1: \ - case 0xc2: \ - case 0xc3: \ - case 0xc4: \ - case 0xc5: \ - case 0xc6: \ - case 0xc7: \ - case 0xc8: \ - case 0xc9: \ - case 0xca: \ - case 0xcb: \ - case 0xcc: \ - case 0xcd: \ - case 0xce: \ - case 0xcf: \ - case 0xd0: \ - case 0xd1: \ - case 0xd2: \ - case 0xd3: \ - case 0xd4: \ - case 0xd5: \ - case 0xd6: \ - case 0xd7: \ - case 0xd8: \ - case 0xd9: \ - case 0xda: \ - case 0xdb: \ - case 0xdc: \ - case 0xdd: \ - case 0xde: \ - case 0xdf: \ - case 0xe0: \ - case 0xe1: \ - case 0xe2: \ - case 0xe3: \ - case 0xe4: \ - case 0xe5: \ - case 0xe6: \ - case 0xe7: \ - case 0xe8: \ - case 0xe9: \ - case 0xea: \ - case 0xeb: \ - case 0xec: \ - case 0xed: \ - case 0xee: \ - case 0xef: \ - case 0xf0: \ - case 0xf1: \ - case 0xf2: \ - case 0xf3: \ - case 0xf4: \ - case 0xf5: \ - case 0xf6: \ - case 0xf7: \ - case 0xf8: \ - case 0xf9: \ - case 0xfa: \ - case 0xfb: \ - case 0xfc: \ - case 0xfd: \ - case 0xfe: \ - case 0xff - -static int is_ws(uint8_t c) { - switch (c) { - case ' ': - case '\t': - return 1; - default: - return 0; - } -} - -static int parser_eof(sf_parser *sfp) { return sfp->pos == sfp->end; } - -static void parser_discard_ows(sf_parser *sfp) { - for (; !parser_eof(sfp) && is_ws(*sfp->pos); ++sfp->pos) - ; -} - -static void parser_discard_sp(sf_parser *sfp) { - for (; !parser_eof(sfp) && *sfp->pos == ' '; ++sfp->pos) - ; -} - -static void parser_set_op_state(sf_parser *sfp, uint32_t op) { - sfp->state &= ~SF_STATE_OP_MASK; - sfp->state |= op; -} - -static void parser_unset_inner_list_state(sf_parser *sfp) { - sfp->state &= ~SF_STATE_INNER_LIST; -} - -static int parser_key(sf_parser *sfp, sf_vec *dest) { - const uint8_t *base; - - switch (*sfp->pos) { - case '*': - LCALPHA_CASES: - break; - default: - return SF_ERR_PARSE_ERROR; - } - - base = sfp->pos++; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - case '_': - case '-': - case '.': - case '*': - DIGIT_CASES: - LCALPHA_CASES: - continue; - } - - break; - } - - if (dest) { - dest->base = (uint8_t *)base; - dest->len = (size_t)(sfp->pos - dest->base); - } - - return 0; -} - -static int parser_number(sf_parser *sfp, sf_value *dest) { - int sign = 1; - int64_t value = 0; - size_t len = 0; - size_t fpos = 0; - - if (*sfp->pos == '-') { - ++sfp->pos; - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - sign = -1; - } - - assert(!parser_eof(sfp)); - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - DIGIT_CASES: - if (++len > 15) { - return SF_ERR_PARSE_ERROR; - } - - value *= 10; - value += *sfp->pos - '0'; - - continue; - } - - break; - } - - if (len == 0) { - return SF_ERR_PARSE_ERROR; - } - - if (parser_eof(sfp) || *sfp->pos != '.') { - if (dest) { - dest->type = SF_TYPE_INTEGER; - dest->flags = SF_VALUE_FLAG_NONE; - dest->integer = value * sign; - } - - return 0; - } - - /* decimal */ - - if (len > 12) { - return SF_ERR_PARSE_ERROR; - } - - fpos = len; - - ++sfp->pos; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - DIGIT_CASES: - if (++len > 15) { - return SF_ERR_PARSE_ERROR; - } - - value *= 10; - value += *sfp->pos - '0'; - - continue; - } - - break; - } - - if (fpos == len || len - fpos > 3) { - return SF_ERR_PARSE_ERROR; - } - - if (dest) { - dest->type = SF_TYPE_DECIMAL; - dest->flags = SF_VALUE_FLAG_NONE; - dest->decimal.numer = value * sign; - - switch (len - fpos) { - case 1: - dest->decimal.denom = 10; - - break; - case 2: - dest->decimal.denom = 100; - - break; - case 3: - dest->decimal.denom = 1000; - - break; - } - } - - return 0; -} - -static int parser_date(sf_parser *sfp, sf_value *dest) { - int rv; - sf_value val; - - /* The first byte has already been validated by the caller. */ - assert('@' == *sfp->pos); - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - rv = parser_number(sfp, &val); - if (rv != 0) { - return rv; - } - - if (val.type != SF_TYPE_INTEGER) { - return SF_ERR_PARSE_ERROR; - } - - if (dest) { - *dest = val; - dest->type = SF_TYPE_DATE; - } - - return 0; -} - -static int parser_string(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - uint32_t flags = SF_VALUE_FLAG_NONE; - - /* The first byte has already been validated by the caller. */ - assert('"' == *sfp->pos); - - base = ++sfp->pos; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - X20_21_CASES: - X23_5B_CASES: - X5D_7E_CASES: - break; - case '\\': - ++sfp->pos; - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - switch (*sfp->pos) { - case '"': - case '\\': - flags = SF_VALUE_FLAG_ESCAPED_STRING; - - break; - default: - return SF_ERR_PARSE_ERROR; - } - - break; - case '"': - if (dest) { - dest->type = SF_TYPE_STRING; - dest->flags = flags; - dest->vec.len = (size_t)(sfp->pos - base); - dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; - } - - ++sfp->pos; - - return 0; - default: - return SF_ERR_PARSE_ERROR; - } - } - - return SF_ERR_PARSE_ERROR; -} - -static int parser_token(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - - /* The first byte has already been validated by the caller. */ - base = sfp->pos++; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - TOKEN_CASES: - continue; - } - - break; - } - - if (dest) { - dest->type = SF_TYPE_TOKEN; - dest->flags = SF_VALUE_FLAG_NONE; - dest->vec.base = (uint8_t *)base; - dest->vec.len = (size_t)(sfp->pos - base); - } - - return 0; -} - -static int parser_byteseq(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - - /* The first byte has already been validated by the caller. */ - assert(':' == *sfp->pos); - - base = ++sfp->pos; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - case '+': - case '/': - DIGIT_CASES: - ALPHA_CASES: - continue; - case '=': - switch ((sfp->pos - base) & 0x3) { - case 0: - case 1: - return SF_ERR_PARSE_ERROR; - case 2: - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - if (*sfp->pos == '=') { - ++sfp->pos; - } - - break; - case 3: - ++sfp->pos; - - break; - } - - if (parser_eof(sfp) || *sfp->pos != ':') { - return SF_ERR_PARSE_ERROR; - } - - goto fin; - case ':': - if (((sfp->pos - base) & 0x3) == 1) { - return SF_ERR_PARSE_ERROR; - } - - goto fin; - default: - return SF_ERR_PARSE_ERROR; - } - } - - return SF_ERR_PARSE_ERROR; - -fin: - if (dest) { - dest->type = SF_TYPE_BYTESEQ; - dest->flags = SF_VALUE_FLAG_NONE; - dest->vec.len = (size_t)(sfp->pos - base); - dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; - } - - ++sfp->pos; - - return 0; -} - -static int parser_boolean(sf_parser *sfp, sf_value *dest) { - int b; - - /* The first byte has already been validated by the caller. */ - assert('?' == *sfp->pos); - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - switch (*sfp->pos) { - case '0': - b = 0; - - break; - case '1': - b = 1; - - break; - default: - return SF_ERR_PARSE_ERROR; - } - - ++sfp->pos; - - if (dest) { - dest->type = SF_TYPE_BOOLEAN; - dest->flags = SF_VALUE_FLAG_NONE; - dest->boolean = b; - } - - return 0; -} - -static int pctdecode(uint8_t *pc, const uint8_t **ppos) { - uint8_t c, b = **ppos; - - switch (b) { - DIGIT_CASES: - c = (uint8_t)((b - '0') << 4); - - break; - LCHEXALPHA_CASES: - c = (uint8_t)((b - 'a' + 10) << 4); - - break; - default: - return -1; - } - - b = *++*ppos; - - switch (b) { - DIGIT_CASES: - c |= (uint8_t)(b - '0'); - - break; - LCHEXALPHA_CASES: - c |= (uint8_t)(b - 'a' + 10); - - break; - default: - return -1; - } - - *pc = c; - ++*ppos; - - return 0; -} - -/* Start of utf8 dfa */ -/* Copyright (c) 2008-2010 Bjoern Hoehrmann - * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. - * - * Copyright (c) 2008-2009 Bjoern Hoehrmann - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#define UTF8_ACCEPT 0 -#define UTF8_REJECT 12 - -/* clang-format off */ -static const uint8_t utf8d[] = { - /* - * The first part of the table maps bytes to character classes that - * to reduce the size of the transition table and create bitmasks. - */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - - /* - * The second part is a transition table that maps a combination - * of a state of the automaton and a character class to a state. - */ - 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, - 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, - 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, - 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, - 12,36,12,12,12,12,12,12,12,12,12,12, -}; -/* clang-format on */ - -static void utf8_decode(uint32_t *state, uint8_t byte) { - *state = utf8d[256 + *state + utf8d[byte]]; -} - -/* End of utf8 dfa */ - -static int parser_dispstring(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - uint8_t c; - uint32_t utf8state = UTF8_ACCEPT; - - assert('%' == *sfp->pos); - - ++sfp->pos; - - if (parser_eof(sfp) || *sfp->pos != '"') { - return SF_ERR_PARSE_ERROR; - } - - base = ++sfp->pos; - - for (; !parser_eof(sfp);) { - switch (*sfp->pos) { - X00_1F_CASES: - X7F_FF_CASES: - return SF_ERR_PARSE_ERROR; - case '%': - ++sfp->pos; - - if (sfp->pos + 2 > sfp->end) { - return SF_ERR_PARSE_ERROR; - } - - if (pctdecode(&c, &sfp->pos) != 0) { - return SF_ERR_PARSE_ERROR; - } - - utf8_decode(&utf8state, c); - if (utf8state == UTF8_REJECT) { - return SF_ERR_PARSE_ERROR; - } - - break; - case '"': - if (utf8state != UTF8_ACCEPT) { - return SF_ERR_PARSE_ERROR; - } - - if (dest) { - dest->type = SF_TYPE_DISPSTRING; - dest->flags = SF_VALUE_FLAG_NONE; - dest->vec.len = (size_t)(sfp->pos - base); - dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; - } - - ++sfp->pos; - - return 0; - default: - if (utf8state != UTF8_ACCEPT) { - return SF_ERR_PARSE_ERROR; - } - - ++sfp->pos; - } - } - - return SF_ERR_PARSE_ERROR; -} - -static int parser_bare_item(sf_parser *sfp, sf_value *dest) { - switch (*sfp->pos) { - case '"': - return parser_string(sfp, dest); - case '-': - DIGIT_CASES: - return parser_number(sfp, dest); - case '@': - return parser_date(sfp, dest); - case ':': - return parser_byteseq(sfp, dest); - case '?': - return parser_boolean(sfp, dest); - case '*': - ALPHA_CASES: - return parser_token(sfp, dest); - case '%': - return parser_dispstring(sfp, dest); - default: - return SF_ERR_PARSE_ERROR; - } -} - -static int parser_skip_inner_list(sf_parser *sfp); - -int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { - int rv; - - switch (sfp->state & SF_STATE_OP_MASK) { - case SF_STATE_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_BEFORE_PARAMS: - parser_set_op_state(sfp, SF_STATE_PARAMS); - - break; - case SF_STATE_PARAMS: - break; - default: - assert(0); - abort(); - } - - if (parser_eof(sfp) || *sfp->pos != ';') { - parser_set_op_state(sfp, SF_STATE_AFTER); - - return SF_ERR_EOF; - } - - ++sfp->pos; - - parser_discard_sp(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - rv = parser_key(sfp, dest_key); - if (rv != 0) { - return rv; - } - - if (parser_eof(sfp) || *sfp->pos != '=') { - if (dest_value) { - dest_value->type = SF_TYPE_BOOLEAN; - dest_value->flags = SF_VALUE_FLAG_NONE; - dest_value->boolean = 1; - } - - return 0; - } - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - return parser_bare_item(sfp, dest_value); -} - -static int parser_skip_params(sf_parser *sfp) { - int rv; - - for (;;) { - rv = sf_parser_param(sfp, NULL, NULL); - switch (rv) { - case 0: - break; - case SF_ERR_EOF: - return 0; - case SF_ERR_PARSE_ERROR: - return rv; - default: - assert(0); - abort(); - } - } -} - -int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) { - int rv; - - switch (sfp->state & SF_STATE_OP_MASK) { - case SF_STATE_BEFORE: - parser_discard_sp(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - break; - case SF_STATE_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* Technically, we are entering SF_STATE_AFTER, but we will set - another state without reading the state. */ - /* parser_set_op_state(sfp, SF_STATE_AFTER); */ - - /* fall through */ - case SF_STATE_AFTER: - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - switch (*sfp->pos) { - case ' ': - parser_discard_sp(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - break; - case ')': - break; - default: - return SF_ERR_PARSE_ERROR; - } - - break; - default: - assert(0); - abort(); - } - - if (*sfp->pos == ')') { - ++sfp->pos; - - parser_unset_inner_list_state(sfp); - parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); - - return SF_ERR_EOF; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); - - return 0; -} - -static int parser_skip_inner_list(sf_parser *sfp) { - int rv; - - for (;;) { - rv = sf_parser_inner_list(sfp, NULL); - switch (rv) { - case 0: - break; - case SF_ERR_EOF: - return 0; - case SF_ERR_PARSE_ERROR: - return rv; - default: - assert(0); - abort(); - } - } -} - -static int parser_next_key_or_item(sf_parser *sfp) { - parser_discard_ows(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_EOF; - } - - if (*sfp->pos != ',') { - return SF_ERR_PARSE_ERROR; - } - - ++sfp->pos; - - parser_discard_ows(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - return 0; -} - -static int parser_dict_value(sf_parser *sfp, sf_value *dest) { - int rv; - - if (parser_eof(sfp) || *(sfp->pos) != '=') { - /* Boolean true */ - if (dest) { - dest->type = SF_TYPE_BOOLEAN; - dest->flags = SF_VALUE_FLAG_NONE; - dest->boolean = 1; - } - - sfp->state = SF_STATE_DICT_BEFORE_PARAMS; - - return 0; - } - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - if (*sfp->pos == '(') { - if (dest) { - dest->type = SF_TYPE_INNER_LIST; - dest->flags = SF_VALUE_FLAG_NONE; - } - - ++sfp->pos; - - sfp->state = SF_STATE_DICT_INNER_LIST_BEFORE; - - return 0; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - sfp->state = SF_STATE_DICT_BEFORE_PARAMS; - - return 0; -} - -int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { - int rv; - - switch (sfp->state) { - case SF_STATE_DICT_INNER_LIST_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_DICT_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_DICT_AFTER: - rv = parser_next_key_or_item(sfp); - if (rv != 0) { - return rv; - } - - break; - case SF_STATE_INITIAL: - parser_discard_sp(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_EOF; - } - - break; - default: - assert(0); - abort(); - } - - rv = parser_key(sfp, dest_key); - if (rv != 0) { - return rv; - } - - return parser_dict_value(sfp, dest_value); -} - -int sf_parser_list(sf_parser *sfp, sf_value *dest) { - int rv; - - switch (sfp->state) { - case SF_STATE_LIST_INNER_LIST_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_LIST_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_LIST_AFTER: - rv = parser_next_key_or_item(sfp); - if (rv != 0) { - return rv; - } - - break; - case SF_STATE_INITIAL: - parser_discard_sp(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_EOF; - } - - break; - default: - assert(0); - abort(); - } - - if (*sfp->pos == '(') { - if (dest) { - dest->type = SF_TYPE_INNER_LIST; - dest->flags = SF_VALUE_FLAG_NONE; - } - - ++sfp->pos; - - sfp->state = SF_STATE_LIST_INNER_LIST_BEFORE; - - return 0; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - sfp->state = SF_STATE_LIST_BEFORE_PARAMS; - - return 0; -} - -int sf_parser_item(sf_parser *sfp, sf_value *dest) { - int rv; - - switch (sfp->state) { - case SF_STATE_INITIAL: - parser_discard_sp(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - break; - case SF_STATE_ITEM_INNER_LIST_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_ITEM_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_ITEM_AFTER: - parser_discard_sp(sfp); - - if (!parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - return SF_ERR_EOF; - default: - assert(0); - abort(); - } - - if (*sfp->pos == '(') { - if (dest) { - dest->type = SF_TYPE_INNER_LIST; - dest->flags = SF_VALUE_FLAG_NONE; - } - - ++sfp->pos; - - sfp->state = SF_STATE_ITEM_INNER_LIST_BEFORE; - - return 0; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - sfp->state = SF_STATE_ITEM_BEFORE_PARAMS; - - return 0; -} - -void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen) { - if (datalen == 0) { - sfp->pos = sfp->end = NULL; - } else { - sfp->pos = data; - sfp->end = data + datalen; - } - - sfp->state = SF_STATE_INITIAL; -} - -void sf_unescape(sf_vec *dest, const sf_vec *src) { - const uint8_t *p, *q; - uint8_t *o; - size_t len, slen; - - if (src->len == 0) { - dest->len = 0; - - return; - } - - o = dest->base; - p = src->base; - len = src->len; - - for (;;) { - q = memchr(p, '\\', len); - if (q == NULL) { - memcpy(o, p, len); - o += len; - - dest->len = (size_t)(o - dest->base); - - return; - } - - slen = (size_t)(q - p); - memcpy(o, p, slen); - o += slen; - - p = q + 1; - *o++ = *p++; - len -= slen + 2; - } -} - -void sf_base64decode(sf_vec *dest, const sf_vec *src) { - static const int index_tbl[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1}; - uint8_t *o; - const uint8_t *p, *end; - uint32_t n; - size_t i, left; - int idx; - - if (src->len == 0) { - dest->len = 0; - - return; - } - - o = dest->base; - p = src->base; - left = src->len & 0x3; - if (left == 0 && src->base[src->len - 1] == '=') { - left = 4; - } - end = src->base + src->len - left; - - for (; p != end;) { - n = 0; - - for (i = 1; i <= 4; ++i, ++p) { - idx = index_tbl[*p]; - - assert(idx != -1); - - n += (uint32_t)(idx << (24 - i * 6)); - } - - *o++ = (uint8_t)(n >> 16); - *o++ = (n >> 8) & 0xffu; - *o++ = n & 0xffu; - } - - switch (left) { - case 0: - goto fin; - case 1: - assert(0); - abort(); - case 3: - if (src->base[src->len - 1] == '=') { - left = 2; - } - - break; - case 4: - assert('=' == src->base[src->len - 1]); - - if (src->base[src->len - 2] == '=') { - left = 2; - } else { - left = 3; - } - - break; - } - - switch (left) { - case 2: - *o = (uint8_t)(index_tbl[*p++] << 2); - *o++ |= (uint8_t)(index_tbl[*p++] >> 4); - - break; - case 3: - n = (uint32_t)(index_tbl[*p++] << 10); - n += (uint32_t)(index_tbl[*p++] << 4); - n += (uint32_t)(index_tbl[*p++] >> 2); - *o++ = (n >> 8) & 0xffu; - *o++ = n & 0xffu; - - break; - } - -fin: - dest->len = (size_t)(o - dest->base); -} - -void sf_pctdecode(sf_vec *dest, const sf_vec *src) { - const uint8_t *p, *q; - uint8_t *o; - size_t len, slen; - - if (src->len == 0) { - dest->len = 0; - - return; - } - - o = dest->base; - p = src->base; - len = src->len; - - for (;;) { - q = memchr(p, '%', len); - if (q == NULL) { - memcpy(o, p, len); - o += len; - - dest->len = (size_t)(o - dest->base); - - return; - } - - slen = (size_t)(q - p); - memcpy(o, p, slen); - o += slen; - - p = q + 1; - - pctdecode(o++, &p); - - len -= slen + 3; - } -} diff --git a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h b/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h index 01cc947d4d61bc..9341221a099438 100644 --- a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h +++ b/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h @@ -31,90 +31,90 @@ libcurl) */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 -#endif +#endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ #ifdef __cplusplus extern "C" { -#endif +#endif /* defined(__cplusplus) */ #if defined(_MSC_VER) && (_MSC_VER < 1800) /* MSVC < 2013 does not have inttypes.h because it is not C99 compliant. See compiler macros and version number in https://sourceforge.net/p/predef/wiki/Compilers/ */ # include -#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ # include -#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ #include #include /** * @enum * - * :type:`sf_type` defines value type. + * :type:`sfparse_type` defines value type. */ -typedef enum sf_type { +typedef enum sfparse_type { /** - * :enum:`SF_TYPE_BOOLEAN` indicates boolean type. + * :enum:`SFPARSE_TYPE_BOOLEAN` indicates boolean type. */ - SF_TYPE_BOOLEAN, + SFPARSE_TYPE_BOOLEAN, /** - * :enum:`SF_TYPE_INTEGER` indicates integer type. + * :enum:`SFPARSE_TYPE_INTEGER` indicates integer type. */ - SF_TYPE_INTEGER, + SFPARSE_TYPE_INTEGER, /** - * :enum:`SF_TYPE_DECIMAL` indicates decimal type. + * :enum:`SFPARSE_TYPE_DECIMAL` indicates decimal type. */ - SF_TYPE_DECIMAL, + SFPARSE_TYPE_DECIMAL, /** - * :enum:`SF_TYPE_STRING` indicates string type. + * :enum:`SFPARSE_TYPE_STRING` indicates string type. */ - SF_TYPE_STRING, + SFPARSE_TYPE_STRING, /** - * :enum:`SF_TYPE_TOKEN` indicates token type. + * :enum:`SFPARSE_TYPE_TOKEN` indicates token type. */ - SF_TYPE_TOKEN, + SFPARSE_TYPE_TOKEN, /** - * :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type. + * :enum:`SFPARSE_TYPE_BYTESEQ` indicates byte sequence type. */ - SF_TYPE_BYTESEQ, + SFPARSE_TYPE_BYTESEQ, /** - * :enum:`SF_TYPE_INNER_LIST` indicates inner list type. + * :enum:`SFPARSE_TYPE_INNER_LIST` indicates inner list type. */ - SF_TYPE_INNER_LIST, + SFPARSE_TYPE_INNER_LIST, /** - * :enum:`SF_TYPE_DATE` indicates date type. + * :enum:`SFPARSE_TYPE_DATE` indicates date type. */ - SF_TYPE_DATE, + SFPARSE_TYPE_DATE, /** - * :enum:`SF_TYPE_DISPSTRING` indicates display string type. + * :enum:`SFPARSE_TYPE_DISPSTRING` indicates display string type. */ - SF_TYPE_DISPSTRING -} sf_type; + SFPARSE_TYPE_DISPSTRING +} sfparse_type; /** * @macro * - * :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has + * :macro:`SFPARSE_ERR_PARSE` indicates fatal parse error has * occurred, and it is not possible to continue the processing. */ -#define SF_ERR_PARSE_ERROR -1 +#define SFPARSE_ERR_PARSE -1 /** * @macro * - * :macro:`SF_ERR_EOF` indicates that there is nothing left to read. - * The context of this error varies depending on the function that - * returns this error code. + * :macro:`SFPARSE_ERR_EOF` indicates that there is nothing left to + * read. The context of this error varies depending on the function + * that returns this error code. */ -#define SF_ERR_EOF -2 +#define SFPARSE_ERR_EOF -2 /** * @struct * - * :type:`sf_vec` stores sequence of bytes. + * :type:`sfparse_vec` stores sequence of bytes. */ -typedef struct sf_vec { +typedef struct sfparse_vec { /** * :member:`base` points to the beginning of the sequence of bytes. */ @@ -123,29 +123,29 @@ typedef struct sf_vec { * :member:`len` is the number of bytes contained in this sequence. */ size_t len; -} sf_vec; +} sfparse_vec; /** * @macro * - * :macro:`SF_VALUE_FLAG_NONE` indicates no flag set. + * :macro:`SFPARSE_VALUE_FLAG_NONE` indicates no flag set. */ -#define SF_VALUE_FLAG_NONE 0x0u +#define SFPARSE_VALUE_FLAG_NONE 0x0u /** * @macro * - * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string + * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` indicates that a string * contains escaped character(s). */ -#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u +#define SFPARSE_VALUE_FLAG_ESCAPED_STRING 0x1u /** * @struct * - * :type:`sf_decimal` contains decimal value. + * :type:`sfparse_decimal` contains decimal value. */ -typedef struct sf_decimal { +typedef struct sfparse_decimal { /** * :member:`numer` contains numerator of the decimal value. */ @@ -154,275 +154,289 @@ typedef struct sf_decimal { * :member:`denom` contains denominator of the decimal value. */ int64_t denom; -} sf_decimal; +} sfparse_decimal; /** * @struct * - * :type:`sf_value` stores a Structured Field item. For Inner List, - * only type is set to :enum:`sf_type.SF_TYPE_INNER_LIST`. In order - * to read the items contained in an inner list, call - * `sf_parser_inner_list`. + * :type:`sfparse_value` stores a Structured Field item. For Inner + * List, only type is set to + * :enum:`sfparse_type.SFPARSE_TYPE_INNER_LIST`. In order to read the + * items contained in an inner list, call `sfparse_parser_inner_list`. */ -typedef struct sf_value { +typedef struct sfparse_value { /** * :member:`type` is the type of the value contained in this * particular object. */ - sf_type type; + sfparse_type type; /** * :member:`flags` is bitwise OR of one or more of - * :macro:`SF_VALUE_FLAG_* `. + * :macro:`SFPARSE_VALUE_FLAG_* `. */ uint32_t flags; /** * @anonunion_start * - * @sf_value_value + * @sfparse_value_value */ union { /** * :member:`boolean` contains boolean value if :member:`type` == - * :enum:`sf_type.SF_TYPE_BOOLEAN`. 1 indicates true, and 0 - * indicates false. + * :enum:`sfparse_type.SFPARSE_TYPE_BOOLEAN`. 1 indicates true, + * and 0 indicates false. */ int boolean; /** * :member:`integer` contains integer value if :member:`type` is - * either :enum:`sf_type.SF_TYPE_INTEGER` or - * :enum:`sf_type.SF_TYPE_DATE`. + * either :enum:`sfparse_type.SFPARSE_TYPE_INTEGER` or + * :enum:`sfparse_type.SFPARSE_TYPE_DATE`. */ int64_t integer; /** * :member:`decimal` contains decimal value if :member:`type` == - * :enum:`sf_type.SF_TYPE_DECIMAL`. + * :enum:`sfparse_type.SFPARSE_TYPE_DECIMAL`. */ - sf_decimal decimal; + sfparse_decimal decimal; /** * :member:`vec` contains sequence of bytes if :member:`type` is - * either :enum:`sf_type.SF_TYPE_STRING`, - * :enum:`sf_type.SF_TYPE_TOKEN`, :enum:`sf_type.SF_TYPE_BYTESEQ`, - * or :enum:`sf_type.SF_TYPE_DISPSTRING`. + * either :enum:`sfparse_type.SFPARSE_TYPE_STRING`, + * :enum:`sfparse_type.SFPARSE_TYPE_TOKEN`, + * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, or + * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`. * - * For :enum:`sf_type.SF_TYPE_STRING`, this field contains one or - * more escaped characters if :member:`flags` has - * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` set. To unescape the - * string, use `sf_unescape`. + * For :enum:`sfparse_type.SFPARSE_TYPE_STRING`, this field + * contains one or more escaped characters if :member:`flags` has + * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` set. To unescape + * the string, use `sfparse_unescape`. * - * For :enum:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64 - * encoded string. To decode this byte string, use - * `sf_base64decode`. + * For :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, this field + * contains base64 encoded string. To decode this byte string, + * use `sfparse_base64decode`. * - * For :enum:`sf_type.SF_TYPE_DISPSTRING`, this field may contain - * percent-encoded UTF-8 byte sequences. To decode it, use - * `sf_pctdecode`. + * For :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`, this field + * may contain percent-encoded UTF-8 byte sequences. To decode + * it, use `sfparse_pctdecode`. * - * If :member:`vec.len ` == 0, :member:`vec.base - * ` is guaranteed to be NULL. + * If :member:`vec.len ` == 0, :member:`vec.base + * ` is guaranteed to be NULL. */ - sf_vec vec; + sfparse_vec vec; /** * @anonunion_end */ }; -} sf_value; +} sfparse_value; /** * @struct * - * :type:`sf_parser` is the Structured Field Values parser. Use - * `sf_parser_init` to initialize it. + * :type:`sfparse_parser` is the Structured Field Values parser. Use + * `sfparse_parser_init` to initialize it. */ -typedef struct sf_parser { +typedef struct sfparse_parser { /* all fields are private */ const uint8_t *pos; const uint8_t *end; uint32_t state; -} sf_parser; +} sfparse_parser; /** * @function * - * `sf_parser_init` initializes |sfp| with the given buffer pointed by - * |data| of length |datalen|. + * `sfparse_parser_init` initializes |sfp| with the given data encoded + * in Structured Field Values pointed by |data| of length |datalen|. */ -void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen); +void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, + size_t datalen); /** * @function * - * `sf_parser_param` reads a parameter. If this function returns 0, - * it stores parameter key and value in |dest_key| and |dest_value| + * `sfparse_parser_param` reads a parameter. If this function returns + * 0, it stores parameter key and value in |dest_key| and |dest_value| * respectively, if they are not NULL. * * This function does no effort to find duplicated keys. Same key may * be reported more than once. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all parameters have - * read, and caller can continue to read rest of the values. If it - * returns :macro:`SF_ERR_PARSE_ERROR`, it encountered fatal error + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all parameters + * have read, and caller can continue to read rest of the values. If + * it returns :macro:`SFPARSE_ERR_PARSE`, it encountered fatal error * while parsing field value. */ -int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); +int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, + sfparse_value *dest_value); /** * @function * - * `sf_parser_dict` reads the next dictionary key and value pair. If - * this function returns 0, it stores the key and value in |dest_key| - * and |dest_value| respectively, if they are not NULL. + * `sfparse_parser_dict` reads the next dictionary key and value pair. + * If this function returns 0, it stores the key and value in + * |dest_key| and |dest_value| respectively, if they are not NULL. * * Caller can optionally read parameters attached to the pair by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * This function does no effort to find duplicated keys. Same key may * be reported more than once. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all key and value - * pairs have been read, and there is nothing left to read. + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all key and + * value pairs have been read, and there is nothing left to read. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * All values in the dictionary have read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); +int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, + sfparse_value *dest_value); /** * @function * - * `sf_parser_list` reads the next list item. If this function + * `sfparse_parser_list` reads the next list item. If this function * returns 0, it stores the item in |dest| if it is not NULL. * * Caller can optionally read parameters attached to the item by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all values in the - * list have been read, and there is nothing left to read. + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in + * the list have been read, and there is nothing left to read. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * All values in the list have read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_list(sf_parser *sfp, sf_value *dest); +int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest); /** * @function * - * `sf_parser_item` reads a single item. If this function returns 0, - * it stores the item in |dest| if it is not NULL. + * `sfparse_parser_item` reads a single item. If this function + * returns 0, it stores the item in |dest| if it is not NULL. * * This function is only used for the field value that consists of a * single item. * * Caller can optionally read parameters attached to the item by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * Caller should call this function again to make sure that there is * nothing left to read. If this 2nd function call returns - * :macro:`SF_ERR_EOF`, all data have been processed successfully. + * :macro:`SFPARSE_ERR_EOF`, all data have been processed + * successfully. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * There is nothing left to read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_item(sf_parser *sfp, sf_value *dest); +int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest); /** * @function * - * `sf_parser_inner_list` reads the next inner list item. If this - * function returns 0, it stores the item in |dest| if it is not NULL. + * `sfparse_parser_inner_list` reads the next inner list item. If + * this function returns 0, it stores the item in |dest| if it is not + * NULL. * * Caller can optionally read parameters attached to the item by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all values in this - * inner list have been read, and caller can optionally read + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in + * this inner list have been read, and caller can optionally read * parameters attached to this inner list by calling - * `sf_parser_param`. Then caller can continue to read rest of the - * values. + * `sfparse_parser_param`. Then caller can continue to read rest of + * the values. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * All values in the inner list have read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_inner_list(sf_parser *sfp, sf_value *dest); +int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest); /** * @function * - * `sf_unescape` copies |src| to |dest| by removing escapes (``\``). - * |src| should be the pointer to :member:`sf_value.vec` of type - * :enum:`sf_type.SF_TYPE_STRING` produced by either `sf_parser_dict`, - * `sf_parser_list`, `sf_parser_inner_list`, `sf_parser_item`, or - * `sf_parser_param`, otherwise the behavior is undefined. + * `sfparse_unescape` copies |src| to |dest| by removing escapes + * (``\``). |src| should be the pointer to + * :member:`sfparse_value.vec` of type + * :enum:`sfparse_type.SFPARSE_TYPE_STRING` produced by either + * `sfparse_parser_dict`, `sfparse_parser_list`, + * `sfparse_parser_inner_list`, `sfparse_parser_item`, or + * `sfparse_parser_param`, otherwise the behavior is undefined. * - * :member:`dest->base ` must point to the buffer that - * has sufficient space to store the unescaped string. + * :member:`dest->base ` must point to the buffer + * that has sufficient space to store the unescaped string. The + * memory areas pointed by :member:`dest->base ` and + * :member:`src->base ` must not overlap. * * This function sets the length of unescaped string to - * :member:`dest->len `. + * :member:`dest->len `. */ -void sf_unescape(sf_vec *dest, const sf_vec *src); +void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src); /** * @function * - * `sf_base64decode` decodes Base64 encoded string |src| and writes - * the result into |dest|. |src| should be the pointer to - * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ` - * produced by either `sf_parser_dict`, `sf_parser_list`, - * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`, - * otherwise the behavior is undefined. + * `sfparse_base64decode` decodes Base64 encoded string |src| and + * writes the result into |dest|. |src| should be the pointer to + * :member:`sfparse_value.vec` of type + * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ` produced by either + * `sfparse_parser_dict`, `sfparse_parser_list`, + * `sfparse_parser_inner_list`, `sfparse_parser_item`, or + * `sfparse_parser_param`, otherwise the behavior is undefined. * - * :member:`dest->base ` must point to the buffer that - * has sufficient space to store the decoded byte string. + * :member:`dest->base ` must point to the buffer + * that has sufficient space to store the decoded byte string. * * This function sets the length of decoded byte string to - * :member:`dest->len `. + * :member:`dest->len `. */ -void sf_base64decode(sf_vec *dest, const sf_vec *src); +void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src); /** * @function * - * `sf_pctdecode` decodes percent-encoded string |src| and writes the - * result into |dest|. |src| should be the pointer to - * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_DISPSTRING` - * produced by either `sf_parser_dict`, `sf_parser_list`, - * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`, - * otherwise the behavior is undefined. + * `sfparse_pctdecode` decodes percent-encoded string |src| and writes + * the result into |dest|. |src| should be the pointer to + * :member:`sfparse_value.vec` of type + * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING` produced by either + * `sfparse_parser_dict`, `sfparse_parser_list`, + * `sfparse_parser_inner_list`, `sfparse_parser_item`, or + * `sfparse_parser_param`, otherwise the behavior is undefined. * - * :member:`dest->base ` must point to the buffer that - * has sufficient space to store the decoded byte string. + * :member:`dest->base ` must point to the buffer + * that has sufficient space to store the decoded byte string. The + * memory areas pointed by :member:`dest->base ` and + * :member:`src->base ` must not overlap. * * This function sets the length of decoded byte string to - * :member:`dest->len `. + * :member:`dest->len `. */ -void sf_pctdecode(sf_vec *dest, const sf_vec *src); +void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src); #ifdef __cplusplus } -#endif +#endif /* defined(__cplusplus) */ -#endif /* SFPARSE_H */ +#endif /* !defined(SFPARSE_H) */ diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index ca64d16778315d..2901eff484ac55 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -9,12 +9,13 @@ 'ngtcp2/lib/ngtcp2_balloc.c', 'ngtcp2/lib/ngtcp2_bbr.c', 'ngtcp2/lib/ngtcp2_buf.c', + 'ngtcp2/lib/ngtcp2_callbacks.c', 'ngtcp2/lib/ngtcp2_cc.c', 'ngtcp2/lib/ngtcp2_cid.c', 'ngtcp2/lib/ngtcp2_conn.c', 'ngtcp2/lib/ngtcp2_conv.c', - 'ngtcp2/lib/ngtcp2_dcidtr.c', 'ngtcp2/lib/ngtcp2_crypto.c', + 'ngtcp2/lib/ngtcp2_dcidtr.c', 'ngtcp2/lib/ngtcp2_err.c', 'ngtcp2/lib/ngtcp2_frame_chain.c', 'ngtcp2/lib/ngtcp2_gaptr.c', @@ -47,8 +48,8 @@ 'ngtcp2/lib/ngtcp2_window_filter.c', 'ngtcp2/crypto/shared.c' ], - 'ngtcp2_sources_quictls': [ - 'ngtcp2/crypto/quictls/quictls.c' + 'ngtcp2_sources_ossl': [ + 'ngtcp2/crypto/ossl/ossl.c' ], 'ngtcp2_sources_boringssl': [ 'ngtcp2/crypto/boringssl/boringssl.c' @@ -56,6 +57,7 @@ 'nghttp3_sources': [ 'nghttp3/lib/nghttp3_balloc.c', 'nghttp3/lib/nghttp3_buf.c', + 'nghttp3/lib/nghttp3_callbacks.c', 'nghttp3/lib/nghttp3_conn.c', 'nghttp3/lib/nghttp3_conv.c', 'nghttp3/lib/nghttp3_debug.c', @@ -76,14 +78,13 @@ 'nghttp3/lib/nghttp3_range.c', 'nghttp3/lib/nghttp3_rcbuf.c', 'nghttp3/lib/nghttp3_ringbuf.c', + 'nghttp3/lib/nghttp3_settings.c', 'nghttp3/lib/nghttp3_str.c', 'nghttp3/lib/nghttp3_stream.c', 'nghttp3/lib/nghttp3_tnode.c', 'nghttp3/lib/nghttp3_unreachable.c', 'nghttp3/lib/nghttp3_vec.c', 'nghttp3/lib/nghttp3_version.c', - # sfparse is also used by nghttp2 and is included by nghttp2.gyp - # 'nghttp3/lib/sfparse.c' ] }, 'targets': [ @@ -128,6 +129,36 @@ 'HAVE_NETINET_IN_H', ], }], + # TODO: Support OpenSSL 3.5 shared library builds. + # The complexity here is that we need to use the ngtcp2 ossl + # adapter, which does not include any conditional checks to + # see if the version of OpenSSL used has the necessary QUIC + # APIs, so we need to ensure that we conditionally enable use + # of the adapter only when we know that the OpenSSL version we + # are compiling against has the necessary APIs. We can do that + # by checkig the OpenSSL version number but, currently, the + # code that does so checks only the VERSION.dat file that is + # bundled with the openssl dependency. We'll need to update + # that to support the shared library case, where the version + # of the shared library needs to be determined. + # + # TODO: Support Boringssl here also. ngtcp2 provides an adapter + # for Boringssl. If we can detect that boringssl is being used + # here then we can use that adapter and also set the + # QUIC_NGTCP2_USE_BORINGSSL define (the guard in quic/guard.h + # would need to be updated to check for this define). + ['node_shared_openssl=="false" and openssl_version >= 0x3050001f', { + 'sources': [ + '<@(ngtcp2_sources_ossl)', + ], + 'direct_dependent_settings': { + 'defines': [ + # Tells us that we are using the OpenSSL 3.5 adapter + # that is provided by ngtcp2. + 'QUIC_NGTCP2_USE_OPENSSL_3_5', + ], + }, + }] ], 'direct_dependent_settings': { 'defines': [ @@ -142,7 +173,6 @@ }, 'sources': [ '<@(ngtcp2_sources)', - '<@(ngtcp2_sources_quictls)', ] }, { diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h index 4eaf615bd309f3..003ec6b4c3f23c 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -69,6 +69,13 @@ extern "C" { */ #define NGTCP2_CRYPTO_ERR_VERIFY_TOKEN -203 +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_ERR_NOMEM` indicates out of memory. + */ +#define NGTCP2_CRYPTO_ERR_NOMEM -501 + /** * @function * diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h new file mode 100644 index 00000000000000..417ec017c60c13 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h @@ -0,0 +1,198 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_OSSL_H +#define NGTCP2_CRYPTO_OSSL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +/** + * @macrosection + * + * ossl specific error codes + */ + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_X509_LOOKUP` is the error + * code which indicates that TLS handshake routine is interrupted by + * X509 certificate lookup. See :macro:`SSL_ERROR_WANT_X509_LOOKUP` + * error description from `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_X509_LOOKUP -10001 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_CLIENT_HELLO_CB` is the + * error code which indicates that TLS handshake routine is + * interrupted by client hello callback. See + * :macro:`SSL_ERROR_WANT_CLIENT_HELLO_CB` error description from + * `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_CLIENT_HELLO_CB -10002 + +/** + * @function + * + * `ngtcp2_crypto_ossl_from_ossl_encryption_level` translates + * |ossl_level| to :type:`ngtcp2_encryption_level`. This function is + * only available for ossl backend. + */ +NGTCP2_EXTERN ngtcp2_encryption_level +ngtcp2_crypto_ossl_from_ossl_encryption_level(uint32_t ossl_level); + +/** + * @function + * + * `ngtcp2_crypto_ossl_from_ngtcp2_encryption_level` translates + * |encryption_level| to OpenSSL encryption level. This function is + * only available for ossl backend. + */ +NGTCP2_EXTERN uint32_t ngtcp2_crypto_ossl_from_ngtcp2_encryption_level( + ngtcp2_encryption_level encryption_level); + +/** + * @struct + * + * :type:`ngtcp2_crypto_ossl_ctx` contains per-connection state, and + * must be set to `ngtcp2_conn_set_tls_native_handle`. + */ +typedef struct ngtcp2_crypto_ossl_ctx ngtcp2_crypto_ossl_ctx; + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_new` creates new + * :type:`ngtcp2_crypto_ossl_ctx` object, and sets it to |*pctx| if it + * succeeds. + * + * |ssl| is set to |*pctx|. It may be NULL, and in that case, call + * `ngtcp2_crypto_ossl_ctx_set_ssl` later to set ``SSL`` object. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_CRYPTO_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_ctx_new(ngtcp2_crypto_ossl_ctx **pctx, + SSL *ssl); + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_del` frees resources allocated for |ctx|. + * It also frees memory pointed by |ctx|. + */ +NGTCP2_EXTERN void ngtcp2_crypto_ossl_ctx_del(ngtcp2_crypto_ossl_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_set_ssl` sets |ssl| to |ctx|. This + * function must be called after ``SSL`` object is created. + */ +NGTCP2_EXTERN void ngtcp2_crypto_ossl_ctx_set_ssl(ngtcp2_crypto_ossl_ctx *ctx, + SSL *ssl); + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_get_ssl` returns ``SSL`` object set to + * |ctx|. If the object has not been set, this function returns NULL. + */ +NGTCP2_EXTERN SSL *ngtcp2_crypto_ossl_ctx_get_ssl(ngtcp2_crypto_ossl_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_ossl_init` initializes libngtcp2_crypto_ossl + * library. This initialization is optional. It is highly + * recommended to call this function before any use of + * libngtcp2_crypto library API to workaround the performance + * regression. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_init(void); + +/** + * @function + * + * `ngtcp2_crypto_ossl_configure_server_session` configures |ssl| for + * server side QUIC connection. It performs the following + * modifications: + * + * - Register callbacks via ``SSL_set_quic_tls_cbs`` + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * Application must call ``SSL_set_app_data(ssl, NULL)`` before + * calling ``SSL_free(ssl)`` if you cannot make `ngtcp2_conn` object + * alive until ``SSL_free`` is called. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_configure_server_session(SSL *ssl); + +/** + * @function + * + * `ngtcp2_crypto_ossl_configure_client_session` configures |ssl| for + * client side QUIC connection. It performs the following + * modifications: + * + * - Register callbacks via ``SSL_set_quic_tls_cbs`` + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * Application must call ``SSL_set_app_data(ssl, NULL)`` before + * calling ``SSL_free(ssl)`` if you cannot make `ngtcp2_conn` object + * alive until ``SSL_free`` is called. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_configure_client_session(SSL *ssl); + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* !defined(NGTCP2_CRYPTO_OSSL_H) */ diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h index d3f2f978e79923..87ea4ba264dd55 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h @@ -37,7 +37,7 @@ extern "C" { * @struct * * :type:`ngtcp2_crypto_picotls_ctx` contains per-connection state of - * Picotls objects and must be an object to bet set to + * Picotls object, and must be set to * `ngtcp2_conn_set_tls_native_handle`. */ typedef struct ngtcp2_crypto_picotls_ctx { diff --git a/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c b/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c new file mode 100644 index 00000000000000..061d6acd21d976 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c @@ -0,0 +1,1191 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ngtcp2_macro.h" +#include "shared.h" + +static int crypto_initialized; +static EVP_CIPHER *crypto_aes_128_gcm; +static EVP_CIPHER *crypto_aes_256_gcm; +static EVP_CIPHER *crypto_chacha20_poly1305; +static EVP_CIPHER *crypto_aes_128_ccm; +static EVP_CIPHER *crypto_aes_128_ctr; +static EVP_CIPHER *crypto_aes_256_ctr; +static EVP_CIPHER *crypto_chacha20; +static EVP_MD *crypto_sha256; +static EVP_MD *crypto_sha384; +static EVP_KDF *crypto_hkdf; + +int ngtcp2_crypto_ossl_init(void) { + crypto_aes_128_gcm = EVP_CIPHER_fetch(NULL, "AES-128-GCM", NULL); + if (crypto_aes_128_gcm == NULL) { + return -1; + } + + crypto_aes_256_gcm = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL); + if (crypto_aes_256_gcm == NULL) { + return -1; + } + + crypto_chacha20_poly1305 = EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL); + if (crypto_chacha20_poly1305 == NULL) { + return -1; + } + + crypto_aes_128_ccm = EVP_CIPHER_fetch(NULL, "AES-128-CCM", NULL); + if (crypto_aes_128_ccm == NULL) { + return -1; + } + + crypto_aes_128_ctr = EVP_CIPHER_fetch(NULL, "AES-128-CTR", NULL); + if (crypto_aes_128_ctr == NULL) { + return -1; + } + + crypto_aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL); + if (crypto_aes_256_ctr == NULL) { + return -1; + } + + crypto_chacha20 = EVP_CIPHER_fetch(NULL, "ChaCha20", NULL); + if (crypto_chacha20 == NULL) { + return -1; + } + + crypto_sha256 = EVP_MD_fetch(NULL, "sha256", NULL); + if (crypto_sha256 == NULL) { + return -1; + } + + crypto_sha384 = EVP_MD_fetch(NULL, "sha384", NULL); + if (crypto_sha384 == NULL) { + return -1; + } + + crypto_hkdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + if (crypto_hkdf == NULL) { + return -1; + } + + crypto_initialized = 1; + + return 0; +} + +static const EVP_CIPHER *crypto_aead_aes_128_gcm(void) { + if (crypto_aes_128_gcm) { + return crypto_aes_128_gcm; + } + + return EVP_aes_128_gcm(); +} + +static const EVP_CIPHER *crypto_aead_aes_256_gcm(void) { + if (crypto_aes_256_gcm) { + return crypto_aes_256_gcm; + } + + return EVP_aes_256_gcm(); +} + +static const EVP_CIPHER *crypto_aead_chacha20_poly1305(void) { + if (crypto_chacha20_poly1305) { + return crypto_chacha20_poly1305; + } + + return EVP_chacha20_poly1305(); +} + +static const EVP_CIPHER *crypto_aead_aes_128_ccm(void) { + if (crypto_aes_128_ccm) { + return crypto_aes_128_ccm; + } + + return EVP_aes_128_ccm(); +} + +static const EVP_CIPHER *crypto_cipher_aes_128_ctr(void) { + if (crypto_aes_128_ctr) { + return crypto_aes_128_ctr; + } + + return EVP_aes_128_ctr(); +} + +static const EVP_CIPHER *crypto_cipher_aes_256_ctr(void) { + if (crypto_aes_256_ctr) { + return crypto_aes_256_ctr; + } + + return EVP_aes_256_ctr(); +} + +static const EVP_CIPHER *crypto_cipher_chacha20(void) { + if (crypto_chacha20) { + return crypto_chacha20; + } + + return EVP_chacha20(); +} + +static const EVP_MD *crypto_md_sha256(void) { + if (crypto_sha256) { + return crypto_sha256; + } + + return EVP_sha256(); +} + +static const EVP_MD *crypto_md_sha384(void) { + if (crypto_sha384) { + return crypto_sha384; + } + + return EVP_sha384(); +} + +static EVP_KDF *crypto_kdf_hkdf(void) { + if (crypto_hkdf) { + return crypto_hkdf; + } + + return EVP_KDF_fetch(NULL, "hkdf", NULL); +} + +static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) { + switch (EVP_CIPHER_nid(aead)) { + case NID_aes_128_gcm: + case NID_aes_256_gcm: + return EVP_GCM_TLS_TAG_LEN; + case NID_chacha20_poly1305: + return EVP_CHACHAPOLY_TLS_TAG_LEN; + case NID_aes_128_ccm: + return EVP_CCM_TLS_TAG_LEN; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)crypto_aead_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)crypto_md_sha256(); + return md; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_aead_aes_128_gcm()); + ctx->md.native_handle = (void *)crypto_md_sha256(); + ctx->hp.native_handle = (void *)crypto_cipher_aes_128_ctr(); + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + aead->native_handle = aead_native_handle; + aead->max_overhead = crypto_aead_max_overhead(aead_native_handle); + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)crypto_aead_aes_128_gcm()); +} + +static const EVP_CIPHER *crypto_cipher_id_get_aead(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + return crypto_aead_aes_128_gcm(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return crypto_aead_aes_256_gcm(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return crypto_aead_chacha20_poly1305(); + case TLS1_3_CK_AES_128_CCM_SHA256: + return crypto_aead_aes_128_ccm(); + default: + return NULL; + } +} + +static uint64_t crypto_cipher_id_get_aead_max_encryption(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + case TLS1_3_CK_AES_128_CCM_SHA256: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM; + default: + return 0; + } +} + +static uint64_t +crypto_cipher_id_get_aead_max_decryption_failure(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + case TLS1_3_CK_AES_128_CCM_SHA256: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM; + default: + return 0; + } +} + +static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return crypto_cipher_aes_128_ctr(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return crypto_cipher_aes_256_ctr(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return crypto_cipher_chacha20(); + default: + return NULL; + } +} + +static const EVP_MD *crypto_cipher_id_get_md(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return crypto_md_sha256(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return crypto_md_sha384(); + default: + return NULL; + } +} + +static int supported_cipher_id(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return 1; + default: + return 0; + } +} + +typedef struct crypto_buf crypto_buf; + +typedef struct crypto_buf { + uint8_t data[4096]; + uint8_t *pos; + uint8_t *last; + + crypto_buf *next; +} crypto_buf; + +static crypto_buf *crypto_buf_new(void) { + crypto_buf *cbuf = malloc(sizeof(crypto_buf)); + + if (cbuf == NULL) { + return NULL; + } + + cbuf->pos = cbuf->last = cbuf->data; + cbuf->next = NULL; + + return cbuf; +} + +static void crypto_buf_del(crypto_buf *cbuf) { free(cbuf); } + +static size_t crypto_buf_left(const crypto_buf *cbuf) { + return (size_t)(cbuf->data + sizeof(cbuf->data) - cbuf->last); +} + +static size_t crypto_buf_len(const crypto_buf *cbuf) { + return (size_t)(cbuf->last - cbuf->pos); +} + +static size_t crypto_buf_eof(const crypto_buf *cbuf) { + return cbuf->pos == cbuf->data + sizeof(cbuf->data); +} + +static void crypto_buf_write(crypto_buf *cbuf, const uint8_t *data, + size_t datalen) { + assert(crypto_buf_left(cbuf) >= datalen); + + memcpy(cbuf->last, data, datalen); + cbuf->last += datalen; +} + +struct ngtcp2_crypto_ossl_ctx { + SSL *ssl; + ngtcp2_encryption_level tx_level; + crypto_buf *crypto_head, *crypto_read, *crypto_write; + size_t crypto_head_released; + uint8_t *remote_params; +}; + +int ngtcp2_crypto_ossl_ctx_new(ngtcp2_crypto_ossl_ctx **possl_ctx, SSL *ssl) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = malloc(sizeof(**possl_ctx)); + + if (ossl_ctx == NULL) { + return NGTCP2_CRYPTO_ERR_NOMEM; + } + + ossl_ctx->ssl = ssl; + ossl_ctx->tx_level = NGTCP2_ENCRYPTION_LEVEL_INITIAL; + ossl_ctx->crypto_head = ossl_ctx->crypto_read = ossl_ctx->crypto_write = NULL; + ossl_ctx->crypto_head_released = 0; + ossl_ctx->remote_params = NULL; + + *possl_ctx = ossl_ctx; + + return 0; +} + +void ngtcp2_crypto_ossl_ctx_del(ngtcp2_crypto_ossl_ctx *ossl_ctx) { + crypto_buf *cbuf, *next; + + if (!ossl_ctx) { + return; + } + + for (cbuf = ossl_ctx->crypto_head; cbuf;) { + next = cbuf->next; + crypto_buf_del(cbuf); + cbuf = next; + } + + free(ossl_ctx->remote_params); + free(ossl_ctx); +} + +void ngtcp2_crypto_ossl_ctx_set_ssl(ngtcp2_crypto_ossl_ctx *ossl_ctx, + SSL *ssl) { + ossl_ctx->ssl = ssl; +} + +SSL *ngtcp2_crypto_ossl_ctx_get_ssl(ngtcp2_crypto_ossl_ctx *ossl_ctx) { + return ossl_ctx->ssl; +} + +static int crypto_ossl_ctx_write_crypto_data(ngtcp2_crypto_ossl_ctx *ossl_ctx, + const uint8_t *data, + size_t datalen) { + crypto_buf *cbuf; + const uint8_t *end; + size_t n, left; + + if (datalen == 0) { + return 0; + } + + if (ossl_ctx->crypto_write == NULL) { + ossl_ctx->crypto_head = ossl_ctx->crypto_read = ossl_ctx->crypto_write = + crypto_buf_new(); + if (ossl_ctx->crypto_head == NULL) { + return NGTCP2_CRYPTO_ERR_NOMEM; + } + } + + for (end = data + datalen; data != end;) { + left = crypto_buf_left(ossl_ctx->crypto_write); + if (left == 0) { + cbuf = crypto_buf_new(); + if (cbuf == NULL) { + return NGTCP2_CRYPTO_ERR_NOMEM; + } + + ossl_ctx->crypto_write->next = cbuf; + ossl_ctx->crypto_write = cbuf; + + left = crypto_buf_left(ossl_ctx->crypto_write); + } + + n = ngtcp2_min_size((size_t)(end - data), left); + crypto_buf_write(ossl_ctx->crypto_write, data, n); + data += n; + } + + return 0; +} + +static void crypto_ossl_ctx_read_crypto_data(ngtcp2_crypto_ossl_ctx *ossl_ctx, + const uint8_t **pbuf, + size_t *pbytes_read) { + size_t n; + + if (ossl_ctx->crypto_read == NULL) { + *pbuf = NULL; + *pbytes_read = 0; + return; + } + + n = crypto_buf_len(ossl_ctx->crypto_read); + + *pbuf = ossl_ctx->crypto_read->pos; + *pbytes_read = n; + + ossl_ctx->crypto_read->pos += n; + + if (crypto_buf_eof(ossl_ctx->crypto_read) && + ossl_ctx->crypto_read != ossl_ctx->crypto_write) { + ossl_ctx->crypto_read = ossl_ctx->crypto_read->next; + } +} + +static void +crypto_ossl_ctx_release_crypto_data(ngtcp2_crypto_ossl_ctx *ossl_ctx, + size_t released) { + crypto_buf *cbuf = NULL; + + if (released == 0) { + return; + } + + assert(ossl_ctx->crypto_head); + + ossl_ctx->crypto_head_released += released; + + for (; ossl_ctx->crypto_head_released >= sizeof(cbuf->data);) { + assert(ossl_ctx->crypto_head); + + cbuf = ossl_ctx->crypto_head; + ossl_ctx->crypto_head = cbuf->next; + + crypto_buf_del(cbuf); + ossl_ctx->crypto_head_released -= sizeof(cbuf->data); + } + + if (cbuf == ossl_ctx->crypto_read) { + ossl_ctx->crypto_read = ossl_ctx->crypto_head; + + if (cbuf == ossl_ctx->crypto_write) { + assert(ossl_ctx->crypto_head == NULL); + + ossl_ctx->crypto_write = NULL; + } + } +} + +static ngtcp2_crypto_ctx *crypto_ctx_cipher_id(ngtcp2_crypto_ctx *ctx, + uint32_t cipher_id) { + ngtcp2_crypto_aead_init(&ctx->aead, + (void *)crypto_cipher_id_get_aead(cipher_id)); + ctx->md.native_handle = (void *)crypto_cipher_id_get_md(cipher_id); + ctx->hp.native_handle = (void *)crypto_cipher_id_get_hp(cipher_id); + ctx->max_encryption = crypto_cipher_id_get_aead_max_encryption(cipher_id); + ctx->max_decryption_failure = + crypto_cipher_id_get_aead_max_decryption_failure(cipher_id); + + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = tls_native_handle; + SSL *ssl = ossl_ctx->ssl; + const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); + uint32_t cipher_id; + + if (cipher == NULL) { + return NULL; + } + + cipher_id = (uint32_t)SSL_CIPHER_get_id(cipher); + + if (!supported_cipher_id(cipher_id)) { + return NULL; + } + + return crypto_ctx_cipher_id(ctx, cipher_id); +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const EVP_MD *md) { + return (size_t)EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_iv_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const EVP_CIPHER *cipher = aead->native_handle; + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); + OSSL_PARAM params[3]; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } + + if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_params(actx, params) || + !EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const EVP_CIPHER *cipher = aead->native_handle; + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); + OSSL_PARAM params[3]; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } + + if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_params(actx, params) || + !EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + EVP_CIPHER_CTX_free(aead_ctx->native_handle); + } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + EVP_CIPHER_CTX *actx; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, cipher->native_handle, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + cipher_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (cipher_ctx->native_handle) { + EVP_CIPHER_CTX_free(cipher_ctx->native_handle); + } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = crypto_kdf_hkdf(); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + if (!crypto_initialized) { + EVP_KDF_free(kdf); + } + + if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = crypto_kdf_hkdf(); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + if (!crypto_initialized) { + EVP_KDF_free(kdf); + } + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = crypto_kdf_hkdf(); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + if (!crypto_initialized) { + EVP_KDF_free(kdf); + } + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_max_overhead(cipher); + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx = aead_ctx->native_handle; + int len; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + dest + plaintextlen, taglen), + OSSL_PARAM_construct_end(), + }; + + (void)noncelen; + + if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) || + (cipher_nid == NID_aes_128_ccm && + !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || + !EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) || + !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || + !EVP_EncryptFinal_ex(actx, dest + len, &len) || + !EVP_CIPHER_CTX_get_params(actx, params)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_max_overhead(cipher); + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx = aead_ctx->native_handle; + int len; + const uint8_t *tag; + OSSL_PARAM params[2]; + + (void)noncelen; + + if (taglen > ciphertextlen) { + return -1; + } + + ciphertextlen -= taglen; + tag = ciphertext + ciphertextlen; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + (void *)tag, taglen); + params[1] = OSSL_PARAM_construct_end(); + + if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) || + !EVP_CIPHER_CTX_set_params(actx, params) || + (cipher_nid == NID_aes_128_ccm && + !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || + !EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) || + !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || + (cipher_nid != NID_aes_128_ccm && + !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + EVP_CIPHER_CTX *actx = hp_ctx->native_handle; + int len; + + (void)hp; + + if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) || + !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) || + !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_read_write_crypto_data( + ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + SSL *ssl = ossl_ctx->ssl; + int rv; + int err; + (void)encryption_level; + + if (crypto_ossl_ctx_write_crypto_data(ossl_ctx, data, datalen) != 0) { + return -1; + } + + if (!ngtcp2_conn_get_handshake_completed(conn)) { + rv = SSL_do_handshake(ssl); + if (rv <= 0) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_WANT_CLIENT_HELLO_CB: + return NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_CLIENT_HELLO_CB; + case SSL_ERROR_WANT_X509_LOOKUP: + return NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_X509_LOOKUP; + case SSL_ERROR_SSL: + return -1; + default: + return -1; + } + } + + ngtcp2_conn_tls_handshake_completed(conn); + } + + rv = SSL_read(ssl, NULL, 0); + if (rv != 1) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + return -1; + default: + return -1; + } + } + + return 0; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { + (void)conn; + (void)tls; + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = tls; + + if (len) { + assert(!ossl_ctx->remote_params); + + ossl_ctx->remote_params = malloc(len); + if (!ossl_ctx->remote_params) { + return -1; + } + + memcpy(ossl_ctx->remote_params, buf, len); + } + + if (SSL_set_quic_tls_transport_params(ossl_ctx->ssl, ossl_ctx->remote_params, + len) != 1) { + return -1; + } + + return 0; +} + +ngtcp2_encryption_level +ngtcp2_crypto_ossl_from_ossl_encryption_level(uint32_t ossl_level) { + switch (ossl_level) { + case OSSL_RECORD_PROTECTION_LEVEL_NONE: + return NGTCP2_ENCRYPTION_LEVEL_INITIAL; + case OSSL_RECORD_PROTECTION_LEVEL_EARLY: + return NGTCP2_ENCRYPTION_LEVEL_0RTT; + case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE: + return NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE; + case OSSL_RECORD_PROTECTION_LEVEL_APPLICATION: + return NGTCP2_ENCRYPTION_LEVEL_1RTT; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +uint32_t ngtcp2_crypto_ossl_from_ngtcp2_encryption_level( + ngtcp2_encryption_level encryption_level) { + switch (encryption_level) { + case NGTCP2_ENCRYPTION_LEVEL_INITIAL: + return OSSL_RECORD_PROTECTION_LEVEL_NONE; + case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: + return OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE; + case NGTCP2_ENCRYPTION_LEVEL_1RTT: + return OSSL_RECORD_PROTECTION_LEVEL_APPLICATION; + case NGTCP2_ENCRYPTION_LEVEL_0RTT: + return OSSL_RECORD_PROTECTION_LEVEL_EARLY; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + if (RAND_bytes(data, (int)datalen) != 1) { + return -1; + } + + return 0; +} + +static int ossl_yield_secret(SSL *ssl, uint32_t ossl_level, int direction, + const unsigned char *secret, size_t secretlen, + void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + ngtcp2_encryption_level level = + ngtcp2_crypto_ossl_from_ossl_encryption_level(ossl_level); + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + if (direction) { + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; + } + + ossl_ctx->tx_level = level; + + return 1; + } + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int ossl_crypto_send(SSL *ssl, const unsigned char *buf, size_t buflen, + size_t *consumed, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + int rv; + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + rv = ngtcp2_conn_submit_crypto_data(conn, ossl_ctx->tx_level, buf, buflen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + *consumed = buflen; + + return 1; +} + +static int ossl_crypto_recv_rcd(SSL *ssl, const unsigned char **buf, + size_t *bytes_read, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + (void)arg; + + if (!conn_ref) { + *buf = NULL; + *bytes_read = 0; + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + crypto_ossl_ctx_read_crypto_data(ossl_ctx, buf, bytes_read); + + return 1; +} + +static int ossl_crypto_release_rcd(SSL *ssl, size_t released, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + (void)arg; + + /* It is sometimes a bit hard or tedious to keep ngtcp2_conn alive + until SSL_free is called. Instead, we require application to + call SSL_set_app_data(ssl, NULL) before SSL_free(ssl) so that + ngtcp2_conn is never used in this function. */ + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + crypto_ossl_ctx_release_crypto_data(ossl_ctx, released); + + return 1; +} + +static int ossl_got_transport_params(SSL *ssl, const unsigned char *params, + size_t paramslen, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + int rv; + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + + rv = + ngtcp2_conn_decode_and_set_remote_transport_params(conn, params, paramslen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int ossl_alert(SSL *ssl, uint8_t alert_code, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + + ngtcp2_conn_set_tls_alert(conn, alert_code); + + return 1; +} + +static const OSSL_DISPATCH qtdis[] = { + { + OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND, + (void (*)(void))ossl_crypto_send, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD, + (void (*)(void))ossl_crypto_recv_rcd, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD, + (void (*)(void))ossl_crypto_release_rcd, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET, + (void (*)(void))ossl_yield_secret, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS, + (void (*)(void))ossl_got_transport_params, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_ALERT, + (void (*)(void))ossl_alert, + }, + OSSL_DISPATCH_END, +}; + +static int crypto_ossl_configure_session(SSL *ssl) { + if (!SSL_set_quic_tls_cbs(ssl, qtdis, NULL)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_ossl_configure_server_session(SSL *ssl) { + return crypto_ossl_configure_session(ssl); +} + +int ngtcp2_crypto_ossl_configure_client_session(SSL *ssl) { + return crypto_ossl_configure_session(ssl); +} diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index d2a2fe1fe1be0d..b0cc867cde0b20 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -214,10 +214,18 @@ typedef struct ngtcp2_mem { /** * @macro * - * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 - * second. + * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to + * 1 nanosecond. + */ +#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL) + +/** + * @macro + * + * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds + * to 1 microsecond. */ -#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL) +#define NGTCP2_MICROSECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_NANOSECONDS)) /** * @macro @@ -225,23 +233,23 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds * to 1 millisecond. */ -#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL) +#define NGTCP2_MILLISECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_MICROSECONDS)) /** * @macro * - * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds - * to 1 microsecond. + * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 + * second. */ -#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL) +#define NGTCP2_SECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_MILLISECONDS)) /** * @macro * - * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to - * 1 nanosecond. + * :macro:`NGTCP2_MINUTES` is a count of tick which corresponds to 1 + * minute. */ -#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL) +#define NGTCP2_MINUTES ((ngtcp2_duration)(60ULL * NGTCP2_SECONDS)) /** * @macrosection @@ -2858,7 +2866,8 @@ typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn, * :type:`ngtcp2_rand` is a callback function to get random data of * length |destlen|. Application must fill random |destlen| bytes to * the buffer pointed by |dest|. The generated data is used only in - * non-cryptographic context. + * non-cryptographic context. But it is strongly recommended to use a + * secure random number generator. */ typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx); @@ -2962,6 +2971,28 @@ typedef int (*ngtcp2_update_key)( */ #define NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN 0x02u +/** + * @functypedef + * + * :type:`ngtcp2_begin_path_validation` is a callback function which + * is called when the path validation has started. |flags| is zero or + * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_* + * `. |path| is the path that is + * being validated. |fallback_path|, if not NULL, is the path that is + * used when this validation fails. + * + * Currently, the flags may only contain + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR`. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_begin_path_validation)(ngtcp2_conn *conn, uint32_t flags, + const ngtcp2_path *path, + const ngtcp2_path *fallback_path, + void *user_data); + /** * @functypedef * @@ -3253,7 +3284,8 @@ typedef int (*ngtcp2_tls_early_data_rejected)(ngtcp2_conn *conn, void *user_data); #define NGTCP2_CALLBACKS_V1 1 -#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V1 +#define NGTCP2_CALLBACKS_V2 2 +#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V2 /** * @struct @@ -3518,6 +3550,13 @@ typedef struct ngtcp2_callbacks { * is only used by client. */ ngtcp2_tls_early_data_rejected tls_early_data_rejected; + /* The following fields have been added since NGTCP2_CALLBACKS_V2. */ + /** + * :member:`begin_path_validation` is a callback function which is + * invoked when a path validation has started. This field is + * available since v1.14.0. + */ + ngtcp2_begin_path_validation begin_path_validation; } ngtcp2_callbacks; /** @@ -4398,6 +4437,17 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, */ #define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` indicates that a + * non-empty 0 RTT or 1 RTT ack-eliciting packet is padded to the + * minimum length of a sending path MTU or a given packet buffer when + * finalizing it. PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE + * only packets and PMTUD packets are excluded. + */ +#define NGTCP2_WRITE_STREAM_FLAG_PADDING 0x04u + /** * @function * @@ -4522,6 +4572,11 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned( * include, call this function with |stream_id| as -1 to stop * coalescing and write a packet. * + * If :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` is set in |flags| when + * finalizing a non-empty 0 RTT or 1 RTT ack-eliciting packet, the + * packet is padded to the minimum length of a sending path MTU or a + * given packet buffer. + * * This function returns 0 if it cannot write any frame because buffer * is too small, or packet is congestion limited. Application should * keep reading and wait for congestion window to grow. @@ -4586,6 +4641,17 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( */ #define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u +/** + * @macro + * + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` indicates that a + * non-empty 0 RTT or 1 RTT ack-eliciting packet is padded to the + * minimum length of a sending path MTU or a given packet buffer when + * finalizing it. PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE + * only packets and PMTUD packets are excluded. + */ +#define NGTCP2_WRITE_DATAGRAM_FLAG_PADDING 0x02u + /** * @function * @@ -4667,6 +4733,11 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( * (or `ngtcp2_conn_writev_stream`) until it returns a positive number * (which indicates a complete packet is ready). * + * If :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` is set in |flags| + * when finalizing a non-empty 0 RTT or 1 RTT ack-eliciting packet, + * the packet is padded to the minimum length of a sending path MTU or + * a given packet buffer. + * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h index bb983f5cf44be3..533f6d5ef6c55d 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "1.11.0" +#define NGTCP2_VERSION "1.14.0" /** * @macro @@ -46,6 +46,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGTCP2_VERSION_NUM 0x010b00 +#define NGTCP2_VERSION_NUM 0x010e00 #endif /* !defined(NGTCP2_VERSION_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c index a20f04521e36ca..a2ffeb6188a57e 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -69,7 +69,7 @@ static void bbr_on_transmit(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr); -static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr); +static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr); static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr); @@ -157,8 +157,7 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); -static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); +static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); @@ -168,15 +167,16 @@ static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr, static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); -static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat); +static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat); -static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack); +static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); -static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); @@ -189,24 +189,20 @@ static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr, static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); -static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -static int is_inflight_too_high(const ngtcp2_rs *rs); +static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr); static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_rs *rs, ngtcp2_tstamp ts); + ngtcp2_tstamp ts); static void bbr_note_loss(ngtcp2_cc_bbr *bbr); static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); -static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_cc_bbr *bbr, - const ngtcp2_rs *rs, - const ngtcp2_cc_pkt *pkt); +static uint64_t +bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr, + const ngtcp2_cc_pkt *pkt); static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); @@ -292,7 +288,7 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, bbr->full_bw_reached = 0; bbr_reset_congestion_signals(bbr); - bbr_reset_lower_bounds(bbr); + bbr_reset_shortterm_model(bbr); bbr_init_round_counting(bbr); bbr_reset_full_bw(bbr); bbr_init_pacing_rate(bbr, cstat); @@ -326,7 +322,7 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, bbr->bw_probe_up_rounds = 0; bbr->bw_probe_up_acks = 0; - bbr->inflight_hi = UINT64_MAX; + bbr->inflight_longterm = UINT64_MAX; bbr->probe_rtt_expired = 0; bbr->probe_rtt_min_delay = UINT64_MAX; @@ -348,9 +344,9 @@ static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr) { bbr->inflight_latest = 0; } -static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr) { - bbr->bw_lo = UINT64_MAX; - bbr->inflight_lo = UINT64_MAX; +static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr) { + bbr->bw_shortterm = UINT64_MAX; + bbr->inflight_shortterm = UINT64_MAX; } static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr) { @@ -367,7 +363,7 @@ static void bbr_reset_full_bw(ngtcp2_cc_bbr *bbr) { static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - if (bbr->full_bw_now || bbr->rst->rs.is_app_limited) { + if (bbr->full_bw_now || !bbr->round_start || bbr->rst->rs.is_app_limited) { return; } @@ -378,10 +374,6 @@ static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr, return; } - if (!bbr->round_start) { - return; - } - ++bbr->full_bw_count; bbr->full_bw_now = bbr->full_bw_count >= 3; @@ -399,36 +391,38 @@ static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) { if (bbr->full_bw_reached || bbr->loss_events_in_round <= 6 || (bbr->in_loss_recovery && bbr->round_count <= bbr->round_count_at_recovery) || - !is_inflight_too_high(&bbr->rst->rs)) { + !bbr_is_inflight_too_high(bbr)) { return; } bbr->full_bw_reached = 1; - bbr->inflight_hi = ngtcp2_max_uint64(bbr_bdp_multiple(bbr, bbr->cwnd_gain_h), - bbr->inflight_latest); + bbr->inflight_longterm = ngtcp2_max_uint64( + bbr_bdp_multiple(bbr, bbr->cwnd_gain_h), bbr->inflight_latest); } static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - cstat->pacing_interval = - (cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS - : cstat->smoothed_rtt) * + cstat->pacing_interval_m = + ((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS + : cstat->smoothed_rtt) + << 10) * 100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd; } static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, uint64_t pacing_gain_h) { - ngtcp2_duration interval; + uint64_t interval_m; if (bbr->bw == 0) { return; } - interval = NGTCP2_SECONDS * 100 * 100 / pacing_gain_h / bbr->bw / - (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT); + interval_m = (NGTCP2_SECONDS << 10) * 100 * 100 / pacing_gain_h / bbr->bw / + (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT); + interval_m = ngtcp2_max_uint64(interval_m, 1); - if (bbr->full_bw_reached || interval < cstat->pacing_interval) { - cstat->pacing_interval = interval; + if (bbr->full_bw_reached || interval_m < cstat->pacing_interval_m) { + cstat->pacing_interval_m = interval_m; } } @@ -546,25 +540,26 @@ static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr, } static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - if (bbr->bw_lo == UINT64_MAX) { - bbr->bw_lo = bbr->max_bw; + if (bbr->bw_shortterm == UINT64_MAX) { + bbr->bw_shortterm = bbr->max_bw; } - if (bbr->inflight_lo == UINT64_MAX) { - bbr->inflight_lo = cstat->cwnd; + if (bbr->inflight_shortterm == UINT64_MAX) { + bbr->inflight_shortterm = cstat->cwnd; } } static void bbr_loss_lower_bounds(ngtcp2_cc_bbr *bbr) { - bbr->bw_lo = ngtcp2_max_uint64( - bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); - bbr->inflight_lo = ngtcp2_max_uint64( + bbr->bw_shortterm = ngtcp2_max_uint64( + bbr->bw_latest, + bbr->bw_shortterm * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); + bbr->inflight_shortterm = ngtcp2_max_uint64( bbr->inflight_latest, - bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); + bbr->inflight_shortterm * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); } static void bbr_bound_bw_for_model(ngtcp2_cc_bbr *bbr) { - bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_lo); + bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_shortterm); } static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, @@ -712,7 +707,7 @@ static void bbr_start_probe_bw_refill(ngtcp2_cc_bbr *bbr) { ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr start ProbeBW_REFILL"); - bbr_reset_lower_bounds(bbr); + bbr_reset_shortterm_model(bbr); bbr->bw_probe_up_rounds = 0; bbr->bw_probe_up_acks = 0; @@ -738,7 +733,7 @@ static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { bbr->pacing_gain_h = 125; bbr->cwnd_gain_h = 225; - bbr_raise_inflight_hi_slope(bbr, cstat); + bbr_raise_inflight_longterm_slope(bbr, cstat); } static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, @@ -749,7 +744,7 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, return; } - bbr_adapt_upper_bounds(bbr, cstat, ack, ts); + bbr_adapt_longterm_model(bbr, cstat, ack); if (!bbr_is_in_probe_bw_state(bbr)) { return; @@ -761,7 +756,7 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, return; } - if (bbr_is_time_to_cruise(bbr, cstat, ts)) { + if (bbr_is_time_to_cruise(bbr, cstat)) { bbr_start_probe_bw_cruise(bbr); } @@ -790,30 +785,22 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, } } -static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - (void)ts; - - if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) { - return 0; - } +static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { + uint64_t inflight = ngtcp2_min_uint64(bbr_inflight_with_headroom(bbr, cstat), + bbr_inflight(bbr, cstat, 100)); - if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) { - return 1; - } - - return 0; + return cstat->bytes_in_flight <= inflight; } static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - if (bbr->rst->is_cwnd_limited && cstat->cwnd >= bbr->inflight_hi) { + if (bbr->rst->is_cwnd_limited && cstat->cwnd >= bbr->inflight_longterm) { bbr_reset_full_bw(bbr); bbr->full_bw = cstat->delivery_rate_sec; - } else if (bbr->full_bw_now) { - return 1; + + return 0; } - return 0; + return bbr->full_bw_now; } static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr, @@ -826,24 +813,25 @@ static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { uint64_t headroom; uint64_t mpcwnd; - if (bbr->inflight_hi == UINT64_MAX) { + if (bbr->inflight_longterm == UINT64_MAX) { return UINT64_MAX; } - headroom = ngtcp2_max_uint64(cstat->max_tx_udp_payload_size, - bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER / - NGTCP2_BBR_HEADROOM_DENOM); + headroom = + ngtcp2_max_uint64(cstat->max_tx_udp_payload_size, + bbr->inflight_longterm * NGTCP2_BBR_HEADROOM_NUMER / + NGTCP2_BBR_HEADROOM_DENOM); mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size); - if (bbr->inflight_hi > headroom) { - return ngtcp2_max_uint64(bbr->inflight_hi - headroom, mpcwnd); + if (bbr->inflight_longterm > headroom) { + return ngtcp2_max_uint64(bbr->inflight_longterm - headroom, mpcwnd); } return mpcwnd; } -static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat) { +static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat) { uint64_t growth_this_round = cstat->max_tx_udp_payload_size << bbr->bw_probe_up_rounds; @@ -851,12 +839,12 @@ static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, bbr->probe_up_cnt = ngtcp2_max_uint64(cstat->cwnd / growth_this_round, 1); } -static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack) { +static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { uint64_t delta; - if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) { + if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_longterm) { return; } @@ -867,16 +855,17 @@ static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, bbr->probe_up_cnt * cstat->max_tx_udp_payload_size) { delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt; bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt; - bbr->inflight_hi += delta; + bbr->inflight_longterm += delta; } if (bbr->round_start) { - bbr_raise_inflight_hi_slope(bbr, cstat); + bbr_raise_inflight_longterm_slope(bbr, cstat); } } -static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { +static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { if (bbr->ack_phase == NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING && bbr->round_start) { bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_FEEDBACK; @@ -889,17 +878,17 @@ static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, } } - if (!bbr_check_inflight_too_high(bbr, cstat, ts)) { - if (bbr->inflight_hi == UINT64_MAX) { + if (!bbr_is_inflight_too_high(bbr)) { + if (bbr->inflight_longterm == UINT64_MAX) { return; } - if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) { - bbr->inflight_hi = bbr->rst->rs.tx_in_flight; + if (bbr->rst->rs.tx_in_flight > bbr->inflight_longterm) { + bbr->inflight_longterm = bbr->rst->rs.tx_in_flight; } if (bbr->state == NGTCP2_BBR_STATE_PROBE_BW_UP) { - bbr_probe_inflight_hi_upward(bbr, cstat, ack); + bbr_probe_inflight_longterm_upward(bbr, cstat, ack); } } } @@ -921,7 +910,7 @@ static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr) { bbr->rand(&rand, 1, &bbr->rand_ctx); - bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256); + bbr->rounds_since_bw_probe = (uint64_t)(rand / 128); bbr->rand(&rand, 1, &bbr->rand_ctx); @@ -941,33 +930,21 @@ static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr, return ngtcp2_min_uint64(bbr->bdp, cstat->cwnd); } -static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - if (is_inflight_too_high(&bbr->rst->rs)) { - if (bbr->bw_probe_samples) { - bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts); - } - - return 1; - } - - return 0; -} - -static int is_inflight_too_high(const ngtcp2_rs *rs) { +static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr) { + const ngtcp2_rs *rs = &bbr->rst->rs; return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM > rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER; } static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_rs *rs, ngtcp2_tstamp ts) { + const ngtcp2_rs *rs = &bbr->rst->rs; + bbr->bw_probe_samples = 0; if (!rs->is_app_limited) { - bbr->inflight_hi = ngtcp2_max_uint64( + bbr->inflight_longterm = ngtcp2_max_uint64( rs->tx_in_flight, bbr_target_inflight(bbr, cstat) * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); } @@ -987,7 +964,7 @@ static void bbr_note_loss(ngtcp2_cc_bbr *bbr) { static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { - ngtcp2_rs rs = {0}; + ngtcp2_rs *rs = &bbr->rst->rs; bbr_note_loss(bbr); @@ -995,22 +972,22 @@ static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, return; } - rs.tx_in_flight = pkt->tx_in_flight; - /* bbr->rst->lost is not incremented for pkt yet */ - assert(bbr->rst->lost + pkt->pktlen >= pkt->lost); - rs.lost = bbr->rst->lost + pkt->pktlen - pkt->lost; - rs.is_app_limited = pkt->is_app_limited; + rs->tx_in_flight = pkt->tx_in_flight; + assert(bbr->rst->lost >= pkt->lost); + rs->lost = bbr->rst->lost - pkt->lost; + rs->is_app_limited = pkt->is_app_limited; - if (is_inflight_too_high(&rs)) { - rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt); + if (bbr_is_inflight_too_high(bbr)) { + rs->tx_in_flight = bbr_inflight_longterm_from_lost_packet(bbr, pkt); - bbr_handle_inflight_too_high(bbr, cstat, &rs, ts); + bbr_handle_inflight_too_high(bbr, cstat, ts); } } -static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_cc_bbr *bbr, - const ngtcp2_rs *rs, - const ngtcp2_cc_pkt *pkt) { +static uint64_t +bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_rs *rs = &bbr->rst->rs; uint64_t inflight_prev, lost_prev, lost_prefix; (void)bbr; @@ -1136,7 +1113,7 @@ static void bbr_mark_connection_app_limited(ngtcp2_cc_bbr *bbr, } static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) { - bbr_reset_lower_bounds(bbr); + bbr_reset_shortterm_model(bbr); if (bbr->full_bw_reached) { bbr_start_probe_bw_down(bbr, ts); @@ -1204,9 +1181,6 @@ static void bbr_update_max_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { uint64_t inflight; - /* Not documented */ - /* bbr_update_aggregation_budget(bbr); */ - inflight = bbr_bdp_multiple(bbr, bbr->cwnd_gain_h) + bbr->extra_acked; bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight); } @@ -1244,12 +1218,9 @@ static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr, static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - uint64_t probe_rtt_cwnd; - if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT) { - probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat); - - cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, probe_rtt_cwnd); + cstat->cwnd = + ngtcp2_min_uint64(cstat->cwnd, bbr_probe_rtt_cwnd(bbr, cstat)); } } @@ -1281,13 +1252,13 @@ static void bbr_bound_cwnd_for_model(ngtcp2_cc_bbr *bbr, if (bbr_is_in_probe_bw_state(bbr) && bbr->state != NGTCP2_BBR_STATE_PROBE_BW_CRUISE) { - cap = bbr->inflight_hi; + cap = bbr->inflight_longterm; } else if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT || bbr->state == NGTCP2_BBR_STATE_PROBE_BW_CRUISE) { cap = bbr_inflight_with_headroom(bbr, cstat); } - cap = ngtcp2_min_uint64(cap, bbr->inflight_lo); + cap = ngtcp2_min_uint64(cap, bbr->inflight_shortterm); cap = ngtcp2_max_uint64(cap, mpcwnd); cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, cap); @@ -1297,10 +1268,9 @@ static void bbr_set_send_quantum(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { size_t send_quantum = 64 * 1024; (void)bbr; - if (cstat->pacing_interval) { - send_quantum = ngtcp2_min_size( - send_quantum, (size_t)(NGTCP2_MILLISECONDS / cstat->pacing_interval)); - } + send_quantum = + ngtcp2_min_size(send_quantum, (size_t)((NGTCP2_MILLISECONDS << 10) / + cstat->pacing_interval_m)); cstat->send_quantum = ngtcp2_max_size(send_quantum, 2 * cstat->max_tx_udp_payload_size); @@ -1420,21 +1390,23 @@ void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx) { - memset(bbr, 0, sizeof(*bbr)); - - bbr->cc.log = log; - bbr->cc.on_pkt_lost = bbr_cc_on_pkt_lost; - bbr->cc.congestion_event = bbr_cc_congestion_event; - bbr->cc.on_spurious_congestion = bbr_cc_on_spurious_congestion; - bbr->cc.on_persistent_congestion = bbr_cc_on_persistent_congestion; - bbr->cc.on_ack_recv = bbr_cc_on_ack_recv; - bbr->cc.on_pkt_sent = bbr_cc_on_pkt_sent; - bbr->cc.reset = bbr_cc_reset; - - bbr->rst = rst; - bbr->rand = rand; - bbr->rand_ctx = *rand_ctx; - bbr->initial_cwnd = cstat->cwnd; + *bbr = (ngtcp2_cc_bbr){ + .cc = + { + .log = log, + .on_pkt_lost = bbr_cc_on_pkt_lost, + .congestion_event = bbr_cc_congestion_event, + .on_spurious_congestion = bbr_cc_on_spurious_congestion, + .on_persistent_congestion = bbr_cc_on_persistent_congestion, + .on_ack_recv = bbr_cc_on_ack_recv, + .on_pkt_sent = bbr_cc_on_pkt_sent, + .reset = bbr_cc_reset, + }, + .rst = rst, + .rand = rand, + .rand_ctx = *rand_ctx, + .initial_cwnd = cstat->cwnd, + }; bbr_on_init(bbr, cstat, initial_ts); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h index 74eb2d640bd3f2..e823711a500654 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -86,8 +86,8 @@ typedef struct ngtcp2_cc_bbr { uint64_t inflight_latest; /* Lower bounds */ - uint64_t bw_lo; - uint64_t inflight_lo; + uint64_t bw_shortterm; + uint64_t inflight_shortterm; /* Round counting */ uint64_t next_round_delivered; @@ -123,7 +123,7 @@ typedef struct ngtcp2_cc_bbr { int bw_probe_samples; size_t bw_probe_up_rounds; uint64_t bw_probe_up_acks; - uint64_t inflight_hi; + uint64_t inflight_longterm; int probe_rtt_expired; ngtcp2_duration probe_rtt_min_delay; ngtcp2_tstamp probe_rtt_min_stamp; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h index e87adb119916ca..b2bdafc3875260 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h @@ -62,13 +62,17 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf); * written to the underlying buffer. In other words, it returns * buf->end - buf->last. */ -#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last) +static inline size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->last); +} /* * ngtcp2_buf_len returns the number of bytes left to read. In other * words, it returns buf->last - buf->pos. */ -#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos) +static inline size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { + return (size_t)(buf->last - buf->pos); +} /* * ngtcp2_buf_cap returns the capacity of the buffer. In other words, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c new file mode 100644 index 00000000000000..7e77186772b762 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c @@ -0,0 +1,72 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_callbacks.h" + +#include +#include + +#include "ngtcp2_unreachable.h" + +static void callbacks_copy(ngtcp2_callbacks *dest, const ngtcp2_callbacks *src, + int callbacks_version) { + assert(callbacks_version != NGTCP2_CALLBACKS_VERSION); + + memcpy(dest, src, ngtcp2_callbackslen_version(callbacks_version)); +} + +const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest( + ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src) { + if (callbacks_version == NGTCP2_CALLBACKS_VERSION) { + return src; + } + + memset(dest, 0, sizeof(*dest)); + + callbacks_copy(dest, src, callbacks_version); + + return dest; +} + +void ngtcp2_callbacks_convert_to_old(int callbacks_version, + ngtcp2_callbacks *dest, + const ngtcp2_callbacks *src) { + assert(callbacks_version != NGTCP2_CALLBACKS_VERSION); + + callbacks_copy(dest, src, callbacks_version); +} + +size_t ngtcp2_callbackslen_version(int callbacks_version) { + ngtcp2_callbacks callbacks; + + switch (callbacks_version) { + case NGTCP2_CALLBACKS_VERSION: + return sizeof(callbacks); + case NGTCP2_CALLBACKS_V1: + return offsetof(ngtcp2_callbacks, tls_early_data_rejected) + + sizeof(callbacks.tls_early_data_rejected); + default: + ngtcp2_unreachable(); + } +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h new file mode 100644 index 00000000000000..751766bb83e5fa --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h @@ -0,0 +1,73 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CALLBACKS_H +#define NGTCP2_CALLBACKS_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +/* + * ngtcp2_callbacks_convert_to_latest converts |src| of version + * |callbacks_version| to the latest version NGTCP2_CALLBACKS_VERSION. + * + * |dest| must point to the latest version. |src| may be the older + * version, and if so, it may have fewer fields. Accessing those + * fields causes undefined behavior. + * + * If |callbacks_version| == NGTCP2_CALLBACKS_VERSION, no conversion + * is made, and |src| is returned. Otherwise, first |dest| is + * zero-initialized, and then all valid fields in |src| are copied + * into |dest|. Finally, |dest| is returned. + */ +const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest( + ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src); + +/* + * ngtcp2_callbacks_convert_to_old converts |src| of the latest + * version to |dest| of version |callbacks_version|. + * + * |callbacks_version| must not be the latest version + * NGTCP2_CALLBACKS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version + * |callbacks_version| from |src| to |dest|. + */ +void ngtcp2_callbacks_convert_to_old(int callbacks_version, + ngtcp2_callbacks *dest, + const ngtcp2_callbacks *src); + +/* + * ngtcp2_callbackslen_version returns the effective length of + * ngtcp2_callbacks at the version |callbacks_version|. + */ +size_t ngtcp2_callbackslen_version(int callbacks_version); + +#endif /* !defined(NGTCP2_CALLBACKS_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c index 508a5d9ec1d8a7..c16953802afd2a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c @@ -59,14 +59,16 @@ ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; } void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) { - memset(reno, 0, sizeof(*reno)); - - reno->cc.log = log; - reno->cc.on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked; - reno->cc.congestion_event = ngtcp2_cc_reno_cc_congestion_event; - reno->cc.on_persistent_congestion = - ngtcp2_cc_reno_cc_on_persistent_congestion; - reno->cc.reset = ngtcp2_cc_reno_cc_reset; + *reno = (ngtcp2_cc_reno){ + .cc = + { + .log = log, + .on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked, + .congestion_event = ngtcp2_cc_reno_cc_congestion_event, + .on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion, + .reset = ngtcp2_cc_reno_cc_reset, + }, + }; reno_cc_reset(reno); } @@ -148,11 +150,10 @@ void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, static void cubic_vars_reset(ngtcp2_cubic_vars *v) { v->cwnd_prior = 0; v->w_max = 0; - v->k = 0; + v->k_m = 0; v->epoch_start = UINT64_MAX; v->w_est = 0; - v->state = NGTCP2_CUBIC_STATE_INITIAL; v->app_limited_start_ts = UINT64_MAX; v->app_limited_duration = 0; v->pending_bytes_delivered = 0; @@ -177,17 +178,18 @@ static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) { void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log, ngtcp2_rst *rst) { - memset(cubic, 0, sizeof(*cubic)); - - cubic->cc.log = log; - cubic->cc.on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv; - cubic->cc.congestion_event = ngtcp2_cc_cubic_cc_congestion_event; - cubic->cc.on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion; - cubic->cc.on_persistent_congestion = - ngtcp2_cc_cubic_cc_on_persistent_congestion; - cubic->cc.reset = ngtcp2_cc_cubic_cc_reset; - - cubic->rst = rst; + *cubic = (ngtcp2_cc_cubic){ + .cc = + { + .log = log, + .on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv, + .congestion_event = ngtcp2_cc_cubic_cc_congestion_event, + .on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion, + .on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion, + .reset = ngtcp2_cc_cubic_cc_reset, + }, + .rst = rst, + }; cubic_cc_reset(cubic); } @@ -228,28 +230,42 @@ static uint64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic, const ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { ngtcp2_duration t = ts - cubic->current.epoch_start; + uint64_t tx_m = (t << 10) / NGTCP2_SECONDS; + int neg = tx_m < cubic->current.k_m; + uint64_t time_delta_m; uint64_t delta; - uint64_t tx = (t << 10) / NGTCP2_SECONDS; - uint64_t kx = (cubic->current.k << 10) / NGTCP2_SECONDS; - uint64_t time_delta; - if (tx < kx) { - return UINT64_MAX; + /* Avoid signed bit-shift */ + if (neg) { + time_delta_m = cubic->current.k_m - tx_m; + } else { + time_delta_m = tx_m - cubic->current.k_m; } - time_delta = tx - kx; + time_delta_m = ngtcp2_min_uint64(time_delta_m, 3600 << 10); + + delta = ((((time_delta_m * time_delta_m) >> 10) * time_delta_m) >> 10) * + cstat->max_tx_udp_payload_size * 4 / 10; + delta >>= 10; - delta = cstat->max_tx_udp_payload_size * - ((((time_delta * time_delta) >> 10) * time_delta) >> 10) * 4 / 10; + if (neg) { + if (cubic->current.w_max < delta) { + /* Negative w_cubic is not interesting. */ + return 0; + } + + return cubic->current.w_max - delta; + } - return cubic->current.w_max + (delta >> 10); + return cubic->current.w_max + delta; } void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); - uint64_t w_cubic, w_cubic_next, target, m; + uint64_t w_cubic, w_cubic_next; + uint64_t target, m; ngtcp2_duration rtt_thresh; int round_start; int is_app_limited = @@ -259,43 +275,27 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, return; } - if (cubic->current.state == NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE) { - if (is_app_limited) { - if (cubic->current.app_limited_start_ts == UINT64_MAX) { - cubic->current.app_limited_start_ts = ts; - } - - return; - } + if (cstat->cwnd < cstat->ssthresh) { + /* slow-start */ + round_start = ack->pkt_delivered >= cubic->next_round_delivered; + if (round_start) { + cubic->next_round_delivered = cubic->rst->delivered; - if (cubic->current.app_limited_start_ts != UINT64_MAX) { - cubic->current.app_limited_duration += - ts - cubic->current.app_limited_start_ts; - cubic->current.app_limited_start_ts = UINT64_MAX; + cubic->rst->is_cwnd_limited = 0; } - } else if (is_app_limited) { - return; - } - - round_start = ack->pkt_delivered >= cubic->next_round_delivered; - if (round_start) { - cubic->next_round_delivered = cubic->rst->delivered; - cubic->rst->is_cwnd_limited = 0; - } + if (!is_app_limited) { + if (cubic->hs.css_round) { + cstat->cwnd += ack->bytes_delivered / NGTCP2_HS_CSS_GROWTH_DIVISOR; + } else { + cstat->cwnd += ack->bytes_delivered; + } - if (cstat->cwnd < cstat->ssthresh) { - /* slow-start */ - if (cubic->hs.css_round) { - cstat->cwnd += ack->bytes_delivered / NGTCP2_HS_CSS_GROWTH_DIVISOR; - } else { - cstat->cwnd += ack->bytes_delivered; + ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, + "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64, + ack->bytes_delivered, cstat->cwnd); } - ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, - "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64, - ack->bytes_delivered, cstat->cwnd); - if (round_start) { cubic->hs.last_round_min_rtt = cubic->hs.current_round_min_rtt; cubic->hs.current_round_min_rtt = UINT64_MAX; @@ -321,7 +321,11 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, "HyStart++ exit slow start"); + cubic->current.epoch_start = ts; + cubic->current.w_max = cstat->cwnd; cstat->ssthresh = cstat->cwnd; + cubic->current.cwnd_prior = cstat->cwnd; + cubic->current.w_est = cstat->cwnd; } return; @@ -347,20 +351,18 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, } /* congestion avoidance */ + if (is_app_limited) { + if (cubic->current.app_limited_start_ts == UINT64_MAX) { + cubic->current.app_limited_start_ts = ts; + } - switch (cubic->current.state) { - case NGTCP2_CUBIC_STATE_INITIAL: - m = cstat->max_tx_udp_payload_size * ack->bytes_delivered + - cubic->current.pending_bytes_delivered; - cstat->cwnd += m / cstat->cwnd; - cubic->current.pending_bytes_delivered = m % cstat->cwnd; return; - case NGTCP2_CUBIC_STATE_RECOVERY: - cubic->current.state = NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE; - cubic->current.epoch_start = ts; - break; - default: - break; + } + + if (cubic->current.app_limited_start_ts != UINT64_MAX) { + cubic->current.app_limited_duration += + ts - cubic->current.app_limited_start_ts; + cubic->current.app_limited_start_ts = UINT64_MAX; } w_cubic = cubic_cc_compute_w_cubic(cubic, cstat, @@ -369,7 +371,7 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic, cstat, ts - cubic->current.app_limited_duration + cstat->smoothed_rtt); - if (w_cubic_next == UINT64_MAX || w_cubic_next < cstat->cwnd) { + if (w_cubic_next < cstat->cwnd) { target = cstat->cwnd; } else if (2 * w_cubic_next > 3 * cstat->cwnd) { target = cstat->cwnd * 3 / 2; @@ -387,19 +389,19 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic->current.w_est += m / cstat->cwnd; } - if (w_cubic == UINT64_MAX || cubic->current.w_est > w_cubic) { + if (cubic->current.w_est > w_cubic) { cstat->cwnd = cubic->current.w_est; } else { m = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size + cubic->current.pending_bytes_delivered; - cstat->cwnd += m / cstat->cwnd; cubic->current.pending_bytes_delivered = m % cstat->cwnd; + cstat->cwnd += m / cstat->cwnd; } ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, "%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64 - " k=%" PRIi64 " target=%" PRIu64 " w_est=%" PRIu64, - ack->bytes_delivered, cstat->cwnd, cubic->current.k, target, + " k_m=%" PRIu64 " target=%" PRIu64 " w_est=%" PRIu64, + ack->bytes_delivered, cstat->cwnd, cubic->current.k_m, target, cubic->current.w_est); } @@ -409,6 +411,7 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); uint64_t flight_size; + uint64_t cwnd_delta; if (in_congestion_recovery(cstat, sent_ts)) { return; @@ -422,8 +425,7 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cstat->congestion_recovery_start_ts = ts; - cubic->current.state = NGTCP2_CUBIC_STATE_RECOVERY; - cubic->current.epoch_start = UINT64_MAX; + cubic->current.epoch_start = ts; cubic->current.app_limited_start_ts = UINT64_MAX; cubic->current.app_limited_duration = 0; cubic->current.pending_bytes_delivered = 0; @@ -435,13 +437,16 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic->current.w_max = cstat->cwnd; } + cubic->current.w_max = + ngtcp2_max_uint64(cubic->current.w_max, 2 * cstat->max_tx_udp_payload_size); + cstat->ssthresh = cstat->cwnd * 7 / 10; if (cubic->rst->rs.delivered * 2 < cstat->cwnd) { flight_size = cstat->bytes_in_flight + bytes_lost; cstat->ssthresh = ngtcp2_min_uint64( cstat->ssthresh, - ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size) * 7 / 10); + ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size)); } cstat->ssthresh = @@ -452,15 +457,12 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic->current.w_est = cstat->cwnd; - if (cstat->cwnd < cubic->current.w_max) { - cubic->current.k = - ngtcp2_cbrt(((cubic->current.w_max - cstat->cwnd) << 10) * 10 / 4 / - cstat->max_tx_udp_payload_size) * - NGTCP2_SECONDS; - cubic->current.k >>= 10; - } else { - cubic->current.k = 0; - } + assert(cubic->current.w_max >= cstat->cwnd); + + cwnd_delta = cubic->current.w_max - cstat->cwnd; + + cubic->current.k_m = + ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 / cstat->max_tx_udp_payload_size); ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, "reduce cwnd because of packet loss cwnd=%" PRIu64, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h index e3c363a51bb85a..e4e19e1aa6b945 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h @@ -326,27 +326,14 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); -typedef enum ngtcp2_cubic_state { - /* NGTCP2_CUBIC_STATE_INITIAL is the state where CUBIC is in slow - start phase, or congestion avoidance phase before congestion - events occur. */ - NGTCP2_CUBIC_STATE_INITIAL, - /* NGTCP2_CUBIC_STATE_RECOVERY is the state that a connection is in - recovery period. */ - NGTCP2_CUBIC_STATE_RECOVERY, - /* NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE is the state where CUBIC - is in congestion avoidance phase after recovery period ends. */ - NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE, -} ngtcp2_cubic_state; - typedef struct ngtcp2_cubic_vars { uint64_t cwnd_prior; uint64_t w_max; - ngtcp2_duration k; + /* CUBIC K with 10 bits extra precision. */ + uint64_t k_m; ngtcp2_tstamp epoch_start; uint64_t w_est; - ngtcp2_cubic_state state; /* app_limited_start_ts is the timestamp where app limited period started. */ ngtcp2_tstamp app_limited_start_ts; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c index 8bff06c0c1f2da..acbee78aaf435f 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c @@ -149,3 +149,9 @@ int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, ? 0 : NGTCP2_ERR_INVALID_ARGUMENT; } + +void ngtcp2_dcid_apply_validated_path(ngtcp2_dcid *dcid, + const ngtcp2_path_history_entry *ent) { + dcid->flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + dcid->max_udp_payload_size = ent->max_udp_payload_size; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h index c6ab16831a38c4..9321cfb64e6daf 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h @@ -176,4 +176,24 @@ int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, const ngtcp2_path *path, const uint8_t *token); +/* TODO It might be performance win if we store congestion state in + this entry, and restore it when migrate back to this path. */ +typedef struct ngtcp2_path_history_entry { + /* ps contains path. */ + ngtcp2_path_storage ps; + /* max_udp_payload_size is the maximum size of UDP datagram payload + that is allowed to be sent to this path. */ + size_t max_udp_payload_size; + /* ts is the timestamp when this entry is added to the path history. + It happens when a local endpoint migrates to the another path. */ + ngtcp2_tstamp ts; +} ngtcp2_path_history_entry; + +/* + * ngtcp2_dcid_apply_validated_path applies the defaults from |ent| + * which contains the validated path and its stored configurations. + */ +void ngtcp2_dcid_apply_validated_path(ngtcp2_dcid *dcid, + const ngtcp2_path_history_entry *ent); + #endif /* !defined(NGTCP2_CID_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c index 59eb90f6ea1afa..393c2281f1ae13 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -39,6 +39,7 @@ #include "ngtcp2_net.h" #include "ngtcp2_transport_params.h" #include "ngtcp2_settings.h" +#include "ngtcp2_callbacks.h" #include "ngtcp2_tstamp.h" #include "ngtcp2_frame_chain.h" @@ -283,6 +284,33 @@ static int conn_call_remove_connection_id(ngtcp2_conn *conn, return 0; } +static int conn_call_begin_path_validation(ngtcp2_conn *conn, + const ngtcp2_pv *pv) { + int rv; + uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE; + const ngtcp2_path *old_path = NULL; + + if (!pv || !conn->callbacks.begin_path_validation) { + return 0; + } + + if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) { + flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR; + } + + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { + old_path = &pv->fallback_dcid.ps.path; + } + + rv = conn->callbacks.begin_path_validation(conn, flags, &pv->dcid.ps.path, + old_path, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv, ngtcp2_path_validation_result res) { int rv; @@ -668,13 +696,13 @@ static int conn_call_recv_tx_key(ngtcp2_conn *conn, return 0; } +// pktns_init initializes |pktns|. It assumes that the object pointed +// by |pktns| is zero-cleared. static void pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, int64_t initial_pkt_num, ngtcp2_log *log, ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { - memset(pktns, 0, sizeof(*pktns)); - ngtcp2_gaptr_init(&pktns->rx.pngap, mem); pktns->tx.last_pkt_num = initial_pkt_num - 1; @@ -696,7 +724,7 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, ngtcp2_log *log, ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { - *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); + *ppktns = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_pktns)); if (*ppktns == NULL) { return NGTCP2_ERR_NOMEM; } @@ -799,7 +827,7 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, cstat->congestion_recovery_start_ts = UINT64_MAX; cstat->bytes_in_flight = 0; cstat->delivery_rate_sec = 0; - cstat->pacing_interval = 0; + cstat->pacing_interval_m = 0; cstat->send_quantum = 64 * 1024; } @@ -1032,6 +1060,56 @@ conn_set_local_transport_params(ngtcp2_conn *conn, p->version_info_present = 1; } +static void conn_update_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + uint8_t gap; + + conn->callbacks.rand(&gap, 1, &conn->local.settings.rand_ctx); + + pktns->tx.skip_pkt.next_pkt_num = + pktns->tx.last_pkt_num + 3 + + (int64_t)gap * (1ll << pktns->tx.skip_pkt.exponent++); + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "next skip pkn=%" PRId64, + pktns->tx.skip_pkt.next_pkt_num); +} + +static int conn_handle_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *rtbent; + ngtcp2_pkt_hd hd; + int rv; + + assert(NGTCP2_PKTNS_ID_APPLICATION == pktns->id); + + if (pktns->tx.last_pkt_num + 1 != pktns->tx.skip_pkt.next_pkt_num) { + return 0; + } + + ngtcp2_pkt_hd_init(&hd, 0, NGTCP2_PKT_1RTT, NULL, NULL, + pktns->tx.skip_pkt.next_pkt_num, 0, 0); + + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, 0, + NGTCP2_RTB_ENTRY_FLAG_SKIP, + &conn->rtb_entry_objalloc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + rv = ngtcp2_rtb_add(&pktns->rtb, rtbent, &conn->cstat); + if (rv != 0) { + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); + return rv; + } + + ++pktns->tx.last_pkt_num; + + conn_update_skip_pkt(conn, pktns); + + return 0; +} + static size_t buflen_align(size_t buflen) { return (buflen + 0x7) & (size_t)~0x7; } @@ -1059,13 +1137,16 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, uint32_t *preferred_versions; ngtcp2_settings settingsbuf; ngtcp2_transport_params paramsbuf; - (void)callbacks_version; + ngtcp2_callbacks callbacksbuf; + uint64_t map_seed; (void)settings_version; settings = ngtcp2_settings_convert_to_latest(&settingsbuf, settings_version, settings); params = ngtcp2_transport_params_convert_to_latest( ¶msbuf, transport_params_version, params); + callbacks = ngtcp2_callbacks_convert_to_latest(&callbacksbuf, + callbacks_version, callbacks); assert(settings->max_window <= NGTCP2_MAX_VARINT); assert(settings->max_stream_window <= NGTCP2_MAX_VARINT); @@ -1167,7 +1248,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem); - ngtcp2_map_init(&(*pconn)->strms, mem); + callbacks->rand((uint8_t *)&map_seed, sizeof(map_seed), &settings->rand_ctx); + ngtcp2_map_init(&(*pconn)->strms, map_seed, mem); ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); @@ -1251,6 +1333,10 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_unreachable(); } + ngtcp2_static_ringbuf_path_history_init(&(*pconn)->path_history); + + (*pconn)->callbacks = *callbacks; + rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, &(*pconn)->cc, settings->initial_pkt_num, &(*pconn)->log, &(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc, @@ -1272,6 +1358,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, &(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); + conn_update_skip_pkt(*pconn, &(*pconn)->pktns); + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); if (scident == NULL) { rv = NGTCP2_ERR_NOMEM; @@ -1378,7 +1466,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->keep_alive.timeout = UINT64_MAX; (*pconn)->oscid = *scid; - (*pconn)->callbacks = *callbacks; (*pconn)->mem = mem; (*pconn)->user_data = user_data; (*pconn)->idle_ts = settings->initial_ts; @@ -2211,7 +2298,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, if (num_reclaimed < 0) { ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); - return rv; + return num_reclaimed; } if (num_reclaimed) { goto build_pkt; @@ -2220,12 +2307,11 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, those packets have been acknowledged (i.e., retransmission in another packet). For server, in this case, we don't have to send any probe packet. Client needs to send probe packets - until it knows that server has completed address validation or - handshake has been confirmed. */ + until it knows that server has completed address + validation. */ if (pktns->rtb.num_pto_eliciting == 0 && (conn->server || - (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | - NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { + (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); /* TODO If packet is empty, we should return now if cwnd is @@ -3036,6 +3122,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_strm *strm; int pkt_empty = 1; uint64_t ndatalen = 0; + uint64_t wdatalen; int send_stream = 0; int stream_blocked = 0; int send_datagram = 0; @@ -3180,6 +3267,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, nfrc->fr.max_data.max_data; } + rv = conn_handle_skip_pkt(conn, pktns, ts); + if (rv != 0) { + return rv; + } + ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), version); @@ -3217,6 +3309,17 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, require_padding = require_padding || !conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE; /* We don't retransmit PATH_RESPONSE. */ + + /* Include PING to make a packet non-probing as per + https://datatracker.ietf.org/doc/html/rfc9000#section-9.3.3 + + An endpoint that receives a PATH_CHALLENGE on an active + path SHOULD send a non-probing packet in response. */ + lfr.type = NGTCP2_FRAME_PING; + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } } } } @@ -3681,7 +3784,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { - return rv; + return num_reclaimed; } if (num_reclaimed) { goto build_pkt; @@ -3704,15 +3807,18 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, rtb_entry_flags |= conn->pkt.rtb_entry_flags; pkt_empty = conn->pkt.pkt_empty; hd_logged = conn->pkt.hd_logged; + require_padding = conn->pkt.require_padding; } left = ngtcp2_ppe_left(ppe); if (*pfrc == NULL && send_stream && ngtcp2_pq_empty(&conn->tx.strmq) && - (ndatalen = ngtcp2_pkt_stream_max_datalen( + (wdatalen = ngtcp2_pkt_stream_max_datalen( vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, left)) != (size_t)-1 && - (ndatalen || datalen == 0)) { + (wdatalen == ndatalen || wdatalen >= NGTCP2_MIN_STREAM_DATALEN) && + (wdatalen || datalen == 0)) { + ndatalen = wdatalen; datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT, vmsg->stream.data, vmsg->stream.datacnt, (size_t)ndatalen); @@ -3898,6 +4004,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->pkt.pkt_empty = pkt_empty; conn->pkt.rtb_entry_flags = rtb_entry_flags; conn->pkt.hd_logged = hd_logged; + conn->pkt.require_padding = require_padding; conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; } @@ -3918,6 +4025,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->pkt.pkt_empty = pkt_empty; conn->pkt.rtb_entry_flags = rtb_entry_flags; conn->pkt.hd_logged = hd_logged; + conn->pkt.require_padding = require_padding; conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; assert(vmsg); @@ -3979,7 +4087,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, /* TODO Push STREAM frame back to ngtcp2_strm if there is an error before ngtcp2_rtb_entry is safely created and added. */ - if (require_padding) { + if ((flags & (NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY)) && + (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + lfr.padding.len = ngtcp2_ppe_padding_size(ppe, destlen); + } else if (require_padding) { lfr.padding.len = ngtcp2_ppe_dgram_padding(ppe); } else { lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); @@ -4130,6 +4241,12 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( version = conn->negotiated_version; cc.ckm = pktns->crypto.tx.ckm; cc.hp_ctx = pktns->crypto.tx.hp_ctx; + + rv = conn_handle_skip_pkt(conn, pktns, ts); + if (rv != 0) { + return rv; + } + break; default: /* We don't support 0-RTT packet in this function. */ @@ -5033,12 +5150,12 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, /* Just freeing memory is dangerous because we might free twice. */ - rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat); + rv = ngtcp2_rtb_reclaim_on_retry(rtb, conn, &conn->pktns, &conn->cstat); if (rv != 0) { return rv; } - rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat); + rv = ngtcp2_rtb_reclaim_on_retry(in_rtb, conn, in_pktns, &conn->cstat); if (rv != 0) { return rv; } @@ -5100,7 +5217,6 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns, pkt_ts, ts); if (num_acked < 0) { - assert(ngtcp2_err_is_fatal((int)num_acked)); return (int)num_acked; } @@ -5432,11 +5548,13 @@ decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp, hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1); - for (i = 0; i < hd->pkt_numlen; ++i) { + for (i = 0; i < 4; ++i) { *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1]; } - hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen); + p -= 4; + hd->pkt_num = ngtcp2_get_pkt_num(p, hd->pkt_numlen); + p += hd->pkt_numlen; return p - dest; } @@ -5625,6 +5743,10 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, } } + if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts); + } + ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); conn_reset_congestion_state(conn, ts); @@ -5699,7 +5821,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, conn->pv = npv; - return 0; + return conn_call_begin_path_validation(conn, conn->pv); } /* @@ -6456,6 +6578,7 @@ static int is_unrecoverable_error(int liberr) { case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: case NGTCP2_ERR_TRANSPORT_PARAM: case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + case NGTCP2_ERR_INTERNAL: return 1; } @@ -6969,23 +7092,23 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && rx_offset == strm->rx.last_offset; - if (fin || datalen) { - if (fin) { - sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; - } - if (!conn_is_tls_handshake_completed(conn)) { - sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; - } - rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, - (size_t)datalen); - if (rv != 0) { - return rv; - } + assert(fin || datalen); - rv = conn_emit_pending_stream_data(conn, strm, rx_offset); - if (rv != 0) { - return rv; - } + if (fin) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; + } + if (!conn_is_tls_handshake_completed(conn)) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; + } + rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, + (size_t)datalen); + if (rv != 0) { + return rv; + } + + rv = conn_emit_pending_stream_data(conn, strm, rx_offset); + if (rv != 0) { + return rv; } } else if (fr->datacnt && !(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len, @@ -7053,9 +7176,9 @@ static void handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams, size_t n) { if ( -#if SIZE_MAX > UINT32_MAX +#if SIZE_MAX == UINT64_MAX NGTCP2_MAX_STREAMS < n || -#endif /* SIZE_MAX > UINT32_MAX */ +#endif /* SIZE_MAX == UINT64_MAX */ *punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) { *punsent_max_remote_streams = NGTCP2_MAX_STREAMS; } else { @@ -7943,7 +8066,12 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { conn->pv = pv; - return conn_call_activate_dcid(conn, &pv->dcid); + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + + return conn_call_begin_path_validation(conn, conn->pv); } /* @@ -8168,13 +8296,14 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, int new_cid_used, ngtcp2_tstamp ts) { ngtcp2_dcid dcid; - ngtcp2_pv *pv; + ngtcp2_pv *pv = NULL; int rv; ngtcp2_duration pto; int require_new_cid; int local_addr_eq; int pref_addr_migration; uint32_t remote_addr_cmp; + const ngtcp2_path_history_entry *validated_path; assert(conn->server); @@ -8198,7 +8327,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, /* Run PMTUD just in case if it is prematurely aborted */ assert(!conn->pmtud); - return conn_start_pmtud(conn); + if (!conn->local.settings.no_pmtud) { + return conn_start_pmtud(conn); + } + + return 0; } remote_addr_cmp = @@ -8268,25 +8401,32 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, dcid.bytes_recv += dgramlen; - pto = conn_compute_pto(conn, &conn->pktns); + validated_path = ngtcp2_conn_find_path_history(conn, path, ts); + if (!validated_path) { + pto = conn_compute_pto(conn, &conn->pktns); - rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto), - NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); - if (rv != 0) { - return rv; - } + rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto), + NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); + if (rv != 0) { + return rv; + } - if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT)) { - ngtcp2_pv_set_fallback(pv, &conn->pv->fallback_dcid, - conn->pv->fallback_pto); - /* Unset the flag bit so that conn_stop_pv does not retire - DCID. */ - conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT; - } else if (!pref_addr_migration) { - ngtcp2_pv_set_fallback(pv, &conn->dcid.current, pto); + if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT)) { + ngtcp2_pv_set_fallback(pv, &conn->pv->fallback_dcid, + conn->pv->fallback_pto); + /* Unset the flag bit so that conn_stop_pv does not retire + DCID. */ + conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT; + } else if (!pref_addr_migration) { + ngtcp2_pv_set_fallback(pv, &conn->dcid.current, pto); + } } - if (!pref_addr_migration) { + if (!pref_addr_migration || validated_path) { + if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts); + } + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_CMP_FLAG_ADDR | @@ -8297,6 +8437,17 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, conn_reset_ecn_validation_state(conn); ngtcp2_conn_stop_pmtud(conn); + + if (validated_path) { + ngtcp2_dcid_apply_validated_path(&conn->dcid.current, validated_path); + + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + } } if (conn->pv) { @@ -8311,7 +8462,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, conn->pv = pv; - return 0; + return conn_call_begin_path_validation(conn, conn->pv); } /* @@ -8485,34 +8636,6 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi, return 0; } -/* - * conn_allow_path_change_under_disable_active_migration returns - * nonzero if a packet from |path| is acceptable under - * disable_active_migration is on. - */ -static int -conn_allow_path_change_under_disable_active_migration(ngtcp2_conn *conn, - const ngtcp2_path *path) { - uint32_t remote_addr_cmp; - - assert(conn->server); - assert(conn->local.transport_params.disable_active_migration); - - /* If local address does not change, it must be passive migration - (NAT rebinding). */ - if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local)) { - remote_addr_cmp = - ngtcp2_addr_cmp(&conn->dcid.current.ps.path.remote, &path->remote); - - return (remote_addr_cmp | NGTCP2_ADDR_CMP_FLAG_PORT) == - NGTCP2_ADDR_CMP_FLAG_PORT; - } - - /* If local address changes, it must be one of the preferred - addresses. */ - return conn_server_preferred_addr_migration(conn, &path->local); -} - /* * conn_recv_pkt processes a packet contained in the buffer pointed by * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets. @@ -8577,30 +8700,6 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, int path_challenge_recved = 0; size_t num_ack_processed = 0; - if (conn->server) { - if (conn->local.transport_params.disable_active_migration && - !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && - !conn_allow_path_change_under_disable_active_migration(conn, path)) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_PKT, - "packet is discarded because local active migration is disabled"); - - return NGTCP2_ERR_DISCARD_PKT; - } - - assert(conn->remote.transport_params); - - if (conn->remote.transport_params->disable_active_migration && - !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local) && - !conn_server_preferred_addr_migration(conn, &path->local)) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_PKT, - "packet is discarded because remote active migration is disabled"); - - return NGTCP2_ERR_DISCARD_PKT; - } - } - if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); if (nread < 0) { @@ -9380,6 +9479,15 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn->dcid.current.bytes_recv += dgramlen; } + if (conn->server && conn->local.transport_params.disable_active_migration && + !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local) && + !conn_server_preferred_addr_migration(conn, &path->local)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet is discarded because active migration is disabled"); + + return 0; + } + while (pktlen) { nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts); if (nread < 0) { @@ -9841,7 +9949,7 @@ static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) { */ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint64_t write_datalen, + uint8_t wflags, uint64_t write_datalen, ngtcp2_tstamp ts) { int rv; ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; @@ -9874,8 +9982,8 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (pending_early_datalen) { early_spktlen = conn_retransmit_retry_early( conn, pi, dest + nwrite, destlen - (size_t)nwrite, (size_t)nwrite, - nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING - : NGTCP2_WRITE_PKT_FLAG_NONE, + wflags | (nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING + : NGTCP2_WRITE_PKT_FLAG_NONE), ts); if (early_spktlen < 0) { @@ -9917,7 +10025,15 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (pending_early_datalen && !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { nwrite = conn_retransmit_retry_early( - conn, pi, dest, destlen, (size_t)res, NGTCP2_WRITE_PKT_FLAG_NONE, ts); + conn, pi, dest, destlen, (size_t)res, + wflags | ((nwrite && + ngtcp2_pkt_get_type_long( + conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version, + *(dest - nwrite)) == NGTCP2_PKT_INITIAL) + ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING + : NGTCP2_WRITE_PKT_FLAG_NONE), + ts); if (nwrite < 0) { return nwrite; } @@ -10069,17 +10185,15 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, * pointed by |dest| if it succeeds, or one of the following negative * error codes: (TBD). */ -static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, - ngtcp2_pkt_info *pi, - uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts) { +static ngtcp2_ssize +conn_client_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, uint8_t wflags, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { int send_stream = 0; int send_datagram = 0; ngtcp2_ssize spktlen, early_spktlen; uint64_t datalen; uint64_t write_datalen = 0; - uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; uint32_t version; @@ -10120,7 +10234,8 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, } if (!ppe_pending) { - spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts); + spktlen = + conn_write_handshake(conn, pi, dest, destlen, wflags, write_datalen, ts); if (spktlen < 0) { return spktlen; @@ -10138,16 +10253,12 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, if (spktlen > 0 && ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) { wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - conn->pkt.require_padding = 1; } } else { assert(!conn->pktns.crypto.rx.ckm); assert(!conn->pktns.crypto.tx.ckm); assert(conn->early.ckm); - if (conn->pkt.require_padding) { - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - } spktlen = conn->pkt.hs_spktlen; } @@ -10736,7 +10847,7 @@ ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { if (conn->in_pktns) { ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb); if (ts != UINT64_MAX) { - ts += conn_compute_pto(conn, conn->in_pktns); + ts += conn_compute_pto(conn, conn->in_pktns) * 3; res = ngtcp2_min_uint64(res, ts); } } @@ -10744,14 +10855,14 @@ ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { if (conn->hs_pktns) { ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb); if (ts != UINT64_MAX) { - ts += conn_compute_pto(conn, conn->hs_pktns); + ts += conn_compute_pto(conn, conn->hs_pktns) * 3; res = ngtcp2_min_uint64(res, ts); } } ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb); if (ts != UINT64_MAX) { - ts += conn_compute_pto(conn, &conn->pktns); + ts += conn_compute_pto(conn, &conn->pktns) * 3; res = ngtcp2_min_uint64(res, ts); } @@ -10759,18 +10870,18 @@ ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { } void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_duration pto; + ngtcp2_duration timeout; if (conn->in_pktns) { - pto = conn_compute_pto(conn, conn->in_pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts); + timeout = conn_compute_pto(conn, conn->in_pktns) * 3; + ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, timeout, ts); } if (conn->hs_pktns) { - pto = conn_compute_pto(conn, conn->hs_pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts); + timeout = conn_compute_pto(conn, conn->hs_pktns) * 3; + ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, timeout, ts); } - pto = conn_compute_pto(conn, &conn->pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); + timeout = conn_compute_pto(conn, &conn->pktns) * 3; + ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, timeout, ts); } /* @@ -11315,27 +11426,34 @@ ngtcp2_ssize ngtcp2_conn_write_stream_versioned( ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, ngtcp2_tstamp ts) { - ngtcp2_vec datav; + ngtcp2_vec datav, *v; + size_t datacnt; - datav.len = datalen; - datav.base = (uint8_t *)data; + if (datalen == 0) { + v = NULL; + datacnt = 0; + } else { + datav.len = datalen; + datav.base = (uint8_t *)data; + v = &datav; + datacnt = 1; + } return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi, dest, destlen, pdatalen, flags, - stream_id, &datav, 1, ts); + stream_id, v, datacnt, ts); } -static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, - ngtcp2_path *path, - int pkt_info_version, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts) { +static ngtcp2_ssize +conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_path *path, + int pkt_info_version, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, uint8_t wflags, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ssize nwrite; nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest, - destlen, vmsg, ts); + destlen, wflags, vmsg, ts); if (nwrite < 0) { return nwrite; } @@ -11364,6 +11482,7 @@ ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( ngtcp2_vmsg vmsg, *pvmsg; ngtcp2_strm *strm; int64_t datalen; + uint8_t wflags; if (pdatalen) { *pdatalen = -1; @@ -11406,8 +11525,14 @@ ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( pvmsg = NULL; } + if (flags & NGTCP2_WRITE_STREAM_FLAG_PADDING) { + wflags = NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY; + } else { + wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + } + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, - destlen, pvmsg, ts); + destlen, wflags, pvmsg, ts); } ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( @@ -11415,14 +11540,22 @@ ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, uint32_t flags, uint64_t dgram_id, const uint8_t *data, size_t datalen, ngtcp2_tstamp ts) { - ngtcp2_vec datav; + ngtcp2_vec datav, *v; + size_t datacnt; - datav.len = datalen; - datav.base = (uint8_t *)data; + if (datalen == 0) { + v = NULL; + datacnt = 0; + } else { + datav.len = datalen; + datav.base = (uint8_t *)data; + v = &datav; + datacnt = 1; + } return ngtcp2_conn_writev_datagram_versioned(conn, path, pkt_info_version, pi, dest, destlen, paccepted, flags, - dgram_id, &datav, 1, ts); + dgram_id, v, datacnt, ts); } ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( @@ -11432,6 +11565,7 @@ ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( ngtcp2_tstamp ts) { ngtcp2_vmsg vmsg; int64_t datalen; + uint8_t wflags; if (paccepted) { *paccepted = 0; @@ -11444,9 +11578,9 @@ ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( datalen = ngtcp2_vec_len_varint(datav, datavcnt); if (datalen == -1 -#if UINT64_MAX > SIZE_MAX +#if SIZE_MAX < UINT64_MAX || (uint64_t)datalen > SIZE_MAX -#endif /* UINT64_MAX > SIZE_MAX */ +#endif /* SIZE_MAX < UINT64_MAX */ ) { return NGTCP2_ERR_INVALID_ARGUMENT; } @@ -11463,19 +11597,25 @@ ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( vmsg.datagram.datacnt = datavcnt; vmsg.datagram.paccepted = paccepted; + if (flags & NGTCP2_WRITE_DATAGRAM_FLAG_PADDING) { + wflags = NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY; + } else { + wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + } + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, - destlen, &vmsg, ts); + destlen, wflags, &vmsg, ts); } ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { + uint8_t wflags, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; size_t origlen; size_t origdestlen = destlen; int rv; - uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ssize res = 0; @@ -11507,7 +11647,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, return conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); } - nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts); + nwrite = + conn_client_write_handshake(conn, pi, dest, destlen, wflags, vmsg, ts); /* We might be unable to write a packet because of depletion of congestion window budget, perhaps due to packet loss that shrinks the window drastically. */ @@ -11575,7 +11716,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, } } - nwrite = conn_write_handshake(conn, pi, dest, destlen, + nwrite = conn_write_handshake(conn, pi, dest, destlen, wflags, /* write_datalen = */ 0, ts); if (nwrite < 0) { return nwrite; @@ -11651,9 +11792,6 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (ppe_pending) { res = conn->pkt.hs_spktlen; - if (conn->pkt.require_padding) { - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - } /* dest and destlen have already been adjusted in ppe in the first run. They are adjusted for probe packet later. */ nwrite = conn_write_pkt(conn, pi, dest, destlen, (size_t)res, vmsg, @@ -12980,6 +13118,7 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, int rv; ngtcp2_dcid dcid; ngtcp2_pv *pv; + const ngtcp2_path_history_entry *validated_path; assert(!conn->server); @@ -13007,24 +13146,45 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, ngtcp2_dcidtr_pop_unused(&conn->dcid.dtr, &dcid); ngtcp2_dcid_set_path(&dcid, path); + if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts); + } + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); - /* TODO It might be better to add a new flag which indicates that a - connection should be closed if this path validation failed. The - current design allows an application to continue, by migrating - into yet another path. */ - rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn), - NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); + validated_path = ngtcp2_conn_find_path_history(conn, path, ts); + if (validated_path) { + ngtcp2_dcid_apply_validated_path(&conn->dcid.current, validated_path); + + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + } else { + /* TODO It might be better to add a new flag which indicates that + a connection should be closed if this path validation failed. + The current design allows an application to continue, by + migrating into yet another path. */ + rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn), + NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); + if (rv != 0) { + return rv; + } + + conn->pv = pv; + } + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { return rv; } - conn->pv = pv; - - return conn_call_activate_dcid(conn, &conn->dcid.current); + return conn_call_begin_path_validation(conn, conn->pv); } int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, @@ -13035,6 +13195,10 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, assert(!conn->server); + if (ngtcp2_conn_find_path_history(conn, path, ts)) { + return ngtcp2_conn_initiate_immediate_migration(conn, path, ts); + } + conn_update_timestamp(conn, ts); rv = conn_initiate_migration_precheck(conn, &path->local); @@ -13060,7 +13224,12 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, conn->pv = pv; - return conn_call_activate_dcid(conn, &pv->dcid); + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + + return conn_call_begin_path_validation(conn, conn->pv); } uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { @@ -13237,7 +13406,7 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, } void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_duration pacing_interval; + uint64_t pacing_interval_m; ngtcp2_duration wait, d; conn_update_timestamp(conn, ts); @@ -13246,24 +13415,24 @@ void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return; } - if (conn->cstat.pacing_interval) { - pacing_interval = conn->cstat.pacing_interval; + if (conn->cstat.pacing_interval_m) { + pacing_interval_m = conn->cstat.pacing_interval_m; } else { /* 1.25 is the under-utilization avoidance factor described in https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */ - pacing_interval = (conn->cstat.first_rtt_sample_ts == UINT64_MAX - ? NGTCP2_MILLISECONDS - : conn->cstat.smoothed_rtt) * - 100 / 125 / conn->cstat.cwnd; + pacing_interval_m = ((conn->cstat.first_rtt_sample_ts == UINT64_MAX + ? NGTCP2_MILLISECONDS + : conn->cstat.smoothed_rtt) + << 10) * + 100 / 125 / conn->cstat.cwnd; + pacing_interval_m = ngtcp2_max_uint64(pacing_interval_m, 1); } - wait = (ngtcp2_duration)(conn->tx.pacing.pktlen * pacing_interval); + wait = (ngtcp2_duration)((conn->tx.pacing.pktlen * pacing_interval_m) >> 10); - if (conn->tx.pacing.compensation >= NGTCP2_MILLISECONDS) { - d = ngtcp2_min_uint64(wait, conn->tx.pacing.compensation); - wait -= d; - conn->tx.pacing.compensation -= d; - } + d = ngtcp2_min_uint64(wait / 2, conn->tx.pacing.compensation); + wait -= d; + conn->tx.pacing.compensation -= d; conn->tx.pacing.next_ts = ts + wait; conn->tx.pacing.pktlen = 0; @@ -13283,6 +13452,37 @@ size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) { return strm->tx.loss_count; } +void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + ngtcp2_tstamp ts) { + ngtcp2_path_history_entry *ent; + + ent = ngtcp2_ringbuf_push_front(&conn->path_history.rb); + ngtcp2_path_storage_init2(&ent->ps, &dcid->ps.path); + ent->max_udp_payload_size = dcid->max_udp_payload_size; + ent->ts = ts; +} + +const ngtcp2_path_history_entry * +ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts) { + ngtcp2_ringbuf *rb = &conn->path_history.rb; + size_t i, len = ngtcp2_ringbuf_len(rb); + ngtcp2_path_history_entry *ent; + + for (i = 0; i < len; ++i) { + ent = ngtcp2_ringbuf_get(rb, i); + if (ngtcp2_tstamp_elapsed(ent->ts, 10 * NGTCP2_MINUTES, ts)) { + return NULL; + } + + if (ngtcp2_path_eq(path, &ent->ps.path)) { + return ent; + } + } + + return NULL; +} + void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, const ngtcp2_path *path, const uint8_t *data) { @@ -13305,7 +13505,7 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( ngtcp2_crypto_km ckm; ngtcp2_crypto_cc cc; ngtcp2_ppe ppe; - ngtcp2_frame fr = {0}; + ngtcp2_frame fr; int rv; ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid, @@ -13336,11 +13536,12 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( return NGTCP2_ERR_NOBUF; } - fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; - fr.connection_close.error_code = error_code; - fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = reasonlen; - fr.connection_close.reason = (uint8_t *)reason; + fr.connection_close = (ngtcp2_connection_close){ + .type = NGTCP2_FRAME_CONNECTION_CLOSE, + .error_code = error_code, + .reasonlen = reasonlen, + .reason = (uint8_t *)reason, + }; rv = ngtcp2_ppe_encode_frame(&ppe, &fr); if (rv != 0) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h index 0ba8d6efcc6ac1..5979d39654b839 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -110,6 +110,9 @@ typedef enum { NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, but it requests to add padding to the full UDP datagram payload size. */ #define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL 0x04u +/* NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY adds padding to the QUIC + packet as much as possible if the packet is not empty. */ +#define NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY 0x08u /* * ngtcp2_max_frame is defined so that it covers the largest ACK @@ -203,6 +206,13 @@ typedef struct ngtcp2_pktns { /* last_pkt_num is the packet number which the local endpoint sent last time.*/ int64_t last_pkt_num; + struct { + /* next_pkt_num is the next packet number to skip. */ + int64_t next_pkt_num; + /* exponent makes gap of skipping packets spread + exponentially. */ + int64_t exponent; + } skip_pkt; ngtcp2_frame_chain *frq; /* non_ack_pkt_start_ts is the timestamp since the local endpoint starts sending continuous non ACK-eliciting packets. */ @@ -308,6 +318,8 @@ typedef struct ngtcp2_early_transport_params { ngtcp2_static_ringbuf_def(path_challenge, 4, sizeof(ngtcp2_path_challenge_entry)) +ngtcp2_static_ringbuf_def(path_history, 4, sizeof(ngtcp2_path_history_entry)) + ngtcp2_objalloc_decl(strm, ngtcp2_strm, oplent) struct ngtcp2_conn { @@ -625,6 +637,10 @@ struct ngtcp2_conn { ngtcp2_cc_cubic cubic; ngtcp2_cc_bbr bbr; }; + /* path_history remembers the paths that have been validated + successfully. The path is added to this history when a local + endpoint migrates to the another path. */ + ngtcp2_static_ringbuf_path_history path_history; const ngtcp2_mem *mem; /* idle_ts is the time instant when idle timer started. */ ngtcp2_tstamp idle_ts; @@ -801,7 +817,8 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts); + uint8_t wflags, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts); /* * ngtcp2_conn_write_single_frame_pkt writes a packet which contains @@ -1094,4 +1111,11 @@ void ngtcp2_conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); */ void ngtcp2_conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); +void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + ngtcp2_tstamp ts); + +const ngtcp2_path_history_entry * +ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts); + #endif /* !defined(NGTCP2_CONN_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h index ad2b7329f48df2..be041b9086081d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h @@ -116,13 +116,13 @@ typedef struct ngtcp2_conn_stat { */ uint64_t delivery_rate_sec; /** - * :member:`pacing_interval` is the inverse of pacing rate, which is - * the current packet sending rate computed by a congestion + * :member:`pacing_interval_m` is the inverse of pacing rate, which + * is the current packet sending rate computed by a congestion * controller. 0 if a congestion controller does not set pacing * interval. Even if this value is set to 0, the library paces - * packets. + * packets. The unit of this value is 1/1024 of nanoseconds. */ - ngtcp2_duration pacing_interval; + uint64_t pacing_interval_m; /** * :member:`send_quantum` is the maximum size of a data aggregate * scheduled and transmitted together. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c index 8a8d7733797176..170b9f803cccec 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c @@ -157,10 +157,11 @@ int ngtcp2_dcidtr_bind_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid **pdest, return 0; } -static int verify_stateless_reset(const ngtcp2_ringbuf *rb, - const ngtcp2_path *path, - const uint8_t *token) { +int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path, + const uint8_t *token) { const ngtcp2_dcid *dcid; + const ngtcp2_ringbuf *rb = &dtr->bound.rb; size_t i, len = ngtcp2_ringbuf_len(rb); for (i = 0; i < len; ++i) { @@ -173,19 +174,6 @@ static int verify_stateless_reset(const ngtcp2_ringbuf *rb, return NGTCP2_ERR_INVALID_ARGUMENT; } -int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr, - const ngtcp2_path *path, - const uint8_t *token) { - int rv; - - rv = verify_stateless_reset(&dtr->retired.rb, path, token); - if (rv == 0) { - return 0; - } - - return verify_stateless_reset(&dtr->bound.rb, path, token); -} - static int verify_token_uniqueness(const ngtcp2_ringbuf *rb, int *pfound, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h index 17942389b814d1..63043427bc0900 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h @@ -340,4 +340,4 @@ int ngtcp2_dcidtr_unused_empty(const ngtcp2_dcidtr *dtr); */ int ngtcp2_dcidtr_bound_full(const ngtcp2_dcidtr *dtr); -#endif /* NGTCP2_DCIDTR_H */ +#endif /* !defined(NGTCP2_DCIDTR_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h index de78bcb8070f53..d5c3f61d2acc7b 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h @@ -292,8 +292,11 @@ void ngtcp2_ksl_clear(ngtcp2_ksl *ksl); /* * ngtcp2_ksl_nth_node returns the |n|th node under |blk|. */ -#define ngtcp2_ksl_nth_node(KSL, BLK, N) \ - ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) +static inline ngtcp2_ksl_node *ngtcp2_ksl_nth_node(const ngtcp2_ksl *ksl, + const ngtcp2_ksl_blk *blk, + size_t n) { + return (ngtcp2_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} #ifndef WIN32 /* @@ -315,18 +318,21 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, * |it| points to. It is undefined to call this function when * ngtcp2_ksl_it_end(it) returns nonzero. */ -#define ngtcp2_ksl_it_get(IT) \ - ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data +static inline void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) { + return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; +} /* * ngtcp2_ksl_it_next advances the iterator by one. It is undefined * if this function is called when ngtcp2_ksl_it_end(it) returns * nonzero. */ -#define ngtcp2_ksl_it_next(IT) \ - (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ - ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ - : 0) +static inline void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it) { + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} /* * ngtcp2_ksl_it_prev moves backward the iterator by one. It is @@ -339,8 +345,9 @@ void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it); * ngtcp2_ksl_it_end returns nonzero if |it| points to the one beyond * the last node. */ -#define ngtcp2_ksl_it_end(IT) \ - ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) +static inline int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} /* * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first @@ -354,8 +361,9 @@ int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it); * It is undefined to call this function when ngtcp2_ksl_it_end(it) * returns nonzero. */ -#define ngtcp2_ksl_it_key(IT) \ - ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) +static inline ngtcp2_ksl_key *ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it) { + return (ngtcp2_ksl_key *)ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->key; +} /* * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c index fc4eb443517d40..e006c22e4e7ff0 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c @@ -594,9 +594,9 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { return; } - memset(&shd, 0, sizeof(shd)); - - shd.type = NGTCP2_PKT_STATELESS_RESET; + shd = (ngtcp2_pkt_hd){ + .type = NGTCP2_PKT_STATELESS_RESET, + }; log->log_printf( log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"), diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c index 5e4726e63ff7ef..246c779f2908ca 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c @@ -33,10 +33,11 @@ #define NGTCP2_INITIAL_HASHBITS 4 -void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { +void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem) { map->mem = mem; map->hashbits = 0; map->table = NULL; + map->seed = seed; map->size = 0; } @@ -77,8 +78,14 @@ int ngtcp2_map_each(const ngtcp2_map *map, int (*func)(void *data, void *ptr), return 0; } -static size_t hash(ngtcp2_map_key_type key, size_t bits) { - return (size_t)((key * 11400714819323198485llu) >> (64 - bits)); +static size_t map_hash(const ngtcp2_map *map, ngtcp2_map_key_type key) { + /* hasher from + https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs + We do not perform finalization here because we use top bits + anyway. */ + key += map->seed; + key *= 0xf1357aea2e62a9c5ull; + return (size_t)((key * 11400714819323198485llu) >> (64 - map->hashbits)); } static void map_bucket_swap(ngtcp2_map_bucket *a, ngtcp2_map_bucket *b) { @@ -109,28 +116,28 @@ void ngtcp2_map_print_distance(const ngtcp2_map *map) { continue; } - idx = hash(bkt->key, map->hashbits); + idx = map_hash(map, bkt->key); fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i, - hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl); + map_hash(map, bkt->key), bkt->key, idx, bkt->psl); } } #endif /* !defined(WIN32) */ -static int insert(ngtcp2_map_bucket *table, size_t hashbits, - ngtcp2_map_key_type key, void *data) { - size_t idx = hash(key, hashbits); +static int map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { + size_t idx = map_hash(map, key); ngtcp2_map_bucket b = { .key = key, .data = data, }; ngtcp2_map_bucket *bkt; - size_t mask = (1u << hashbits) - 1; + size_t mask = (1u << map->hashbits) - 1; for (;;) { - bkt = &table[idx]; + bkt = &map->table[idx]; if (bkt->data == NULL) { *bkt = b; + ++map->size; return 0; } @@ -151,15 +158,19 @@ static int insert(ngtcp2_map_bucket *table, size_t hashbits, static int map_resize(ngtcp2_map *map, size_t new_hashbits) { size_t i; - ngtcp2_map_bucket *new_table; ngtcp2_map_bucket *bkt; size_t tablelen; int rv; + ngtcp2_map new_map = { + .table = ngtcp2_mem_calloc(map->mem, 1u << new_hashbits, + sizeof(ngtcp2_map_bucket)), + .mem = map->mem, + .seed = map->seed, + .hashbits = new_hashbits, + }; (void)rv; - new_table = - ngtcp2_mem_calloc(map->mem, 1u << new_hashbits, sizeof(ngtcp2_map_bucket)); - if (new_table == NULL) { + if (new_map.table == NULL) { return NGTCP2_ERR_NOMEM; } @@ -172,15 +183,15 @@ static int map_resize(ngtcp2_map *map, size_t new_hashbits) { continue; } - rv = insert(new_table, new_hashbits, bkt->key, bkt->data); + rv = map_insert(&new_map, bkt->key, bkt->data); assert(0 == rv); } } ngtcp2_mem_free(map->mem, map->table); + map->table = new_map.table; map->hashbits = new_hashbits; - map->table = new_table; return 0; } @@ -190,10 +201,10 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { assert(data); - /* Load factor is 0.75 */ + /* Load factor is 7/8 */ /* Under the very initial condition, that is map->size == 0 and - map->hashbits == 0, 4 > 3 still holds nicely. */ - if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) { + map->hashbits == 0, 8 > 7 still holds nicely. */ + if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) { if (map->hashbits) { rv = map_resize(map, map->hashbits + 1); if (rv != 0) { @@ -207,13 +218,11 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { } } - rv = insert(map->table, map->hashbits, key, data); + rv = map_insert(map, key, data); if (rv != 0) { return rv; } - ++map->size; - return 0; } @@ -227,7 +236,7 @@ void *ngtcp2_map_find(const ngtcp2_map *map, ngtcp2_map_key_type key) { return NULL; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { @@ -256,7 +265,7 @@ int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) { return NGTCP2_ERR_INVALID_ARGUMENT; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h index 9d882fb20088d8..0c1a76552aa4c4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h @@ -47,6 +47,7 @@ typedef struct ngtcp2_map_bucket { typedef struct ngtcp2_map { ngtcp2_map_bucket *table; const ngtcp2_mem *mem; + uint64_t seed; size_t size; size_t hashbits; } ngtcp2_map; @@ -54,7 +55,7 @@ typedef struct ngtcp2_map { /* * ngtcp2_map_init initializes the map |map|. */ -void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); +void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem); /* * ngtcp2_map_free deallocates any resources allocated for |map|. The diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c index d78978492ca8e7..af8a059d08f7bb 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -304,11 +304,11 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } ngtcp2_get_uvarint(&vi, p); -#if SIZE_MAX > UINT32_MAX +#if SIZE_MAX < UINT64_MAX if (vi > SIZE_MAX) { return NGTCP2_ERR_INVALID_ARGUMENT; } -#endif /* SIZE_MAX > UINT32_MAX */ +#endif /* SIZE_MAX < UINT64_MAX */ longlen = (size_t)vi; } @@ -2080,8 +2080,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, } for (i = 0; i < fr->datacnt; ++i) { - assert(fr->data[i].len); - assert(fr->data[i].base); + if (fr->data[i].len == 0) { + continue; + } + p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); } @@ -2421,9 +2423,7 @@ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, left -= n; if (left > 8 + 1073741823 && len > 1073741823) { -#if SIZE_MAX > UINT32_MAX len = ngtcp2_min_uint64(len, 4611686018427387903lu); -#endif /* SIZE_MAX > UINT32_MAX */ return (size_t)ngtcp2_min_uint64(len, (uint64_t)(left - 8)); } @@ -2453,9 +2453,9 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { left -= n; if (left > 8 + 1073741823 && len > 1073741823) { -#if SIZE_MAX > UINT32_MAX +#if SIZE_MAX == UINT64_MAX len = ngtcp2_min_size(len, 4611686018427387903lu); -#endif /* SIZE_MAX > UINT32_MAX */ +#endif /* SIZE_MAX == UINT64_MAX */ return ngtcp2_min_size(len, left - 8); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h index 756076e7a7f258..ec72708e0426e7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -137,6 +137,13 @@ v2. */ #define NGTCP2_PKT_TYPE_RETRY_V2 0x0 +/* NGTCP2_MIN_STREAM_DATALEN is the minimum length of STREAM frame to + avoid too small frame. It is not always enforced for various + reasons. For example, due to flow control, we might have fewer + bytes available to send. Therefore, it is only applied when the + length of data to send is larger than this limit. */ +#define NGTCP2_MIN_STREAM_DATALEN 256 + typedef struct ngtcp2_pkt_retry { ngtcp2_cid odcid; uint8_t *token; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c index 13ef2b24908905..4d193125ae8e89 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c @@ -47,9 +47,10 @@ void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { ngtcp2_ssize rv; ngtcp2_buf *buf = &ppe->buf; + size_t buf_left = ngtcp2_buf_left(buf); ngtcp2_crypto_cc *cc = ppe->cc; - if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) { + if (buf_left <= cc->aead.max_overhead) { return NGTCP2_ERR_NOBUF; } @@ -62,13 +63,13 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN; - rv = ngtcp2_pkt_encode_hd_long( - buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); + rv = ngtcp2_pkt_encode_hd_long(buf->last, buf_left - cc->aead.max_overhead, + hd); } else { ppe->pkt_num_offset = 1 + hd->dcid.datalen; - rv = ngtcp2_pkt_encode_hd_short( - buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); + rv = ngtcp2_pkt_encode_hd_short(buf->last, buf_left - cc->aead.max_overhead, + hd); } if (rv < 0) { @@ -87,14 +88,14 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) { ngtcp2_ssize rv; ngtcp2_buf *buf = &ppe->buf; + size_t buf_left = ngtcp2_buf_left(buf); ngtcp2_crypto_cc *cc = ppe->cc; - if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) { + if (buf_left <= cc->aead.max_overhead) { return NGTCP2_ERR_NOBUF; } - rv = ngtcp2_pkt_encode_frame( - buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, fr); + rv = ngtcp2_pkt_encode_frame(buf->last, buf_left - cc->aead.max_overhead, fr); if (rv < 0) { return (int)rv; } @@ -172,18 +173,13 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { size_t ngtcp2_ppe_left(const ngtcp2_ppe *ppe) { ngtcp2_crypto_cc *cc = ppe->cc; + size_t buf_left = ngtcp2_buf_left(&ppe->buf); - if (ngtcp2_buf_left(&ppe->buf) < cc->aead.max_overhead) { + if (buf_left <= cc->aead.max_overhead) { return 0; } - return ngtcp2_buf_left(&ppe->buf) - cc->aead.max_overhead; -} - -size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe) { - ngtcp2_crypto_cc *cc = ppe->cc; - - return ngtcp2_buf_len(&ppe->buf) + cc->aead.max_overhead; + return buf_left - cc->aead.max_overhead; } size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) { @@ -208,6 +204,10 @@ size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) { assert(ngtcp2_buf_left(buf) >= len + cc->aead.max_overhead); + if (len == 0) { + return 0; + } + buf->last = ngtcp2_setmem(buf->last, 0, len); return len; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h index 660b1482b56671..9874b36800540d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h @@ -110,12 +110,6 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt); */ size_t ngtcp2_ppe_left(const ngtcp2_ppe *ppe); -/* - * ngtcp2_ppe_pktlen returns the provisional packet length. It - * includes AEAD overhead. - */ -size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe); - /* * ngtcp2_ppe_dgram_padding is equivalent to call * ngtcp2_ppe_dgram_padding_size(ppe, NGTCP2_MAX_UDP_PAYLOAD_SIZE). diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h index d490524805b1e9..73efb5255fcf44 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h @@ -110,7 +110,9 @@ void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len); void *ngtcp2_ringbuf_get(const ngtcp2_ringbuf *rb, size_t offset); /* ngtcp2_ringbuf_len returns the number of elements stored. */ -#define ngtcp2_ringbuf_len(RB) ((RB)->len) +static inline size_t ngtcp2_ringbuf_len(const ngtcp2_ringbuf *rb) { + return rb->len; +} /* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ int ngtcp2_ringbuf_full(const ngtcp2_ringbuf *rb); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c index 181691f3e69401..6f44c8ac8de673 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c @@ -38,7 +38,6 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) { rs->prior_ts = UINT64_MAX; rs->tx_in_flight = 0; rs->lost = 0; - rs->prior_lost = 0; rs->send_elapsed = 0; rs->ack_elapsed = 0; rs->last_end_seq = -1; @@ -58,7 +57,6 @@ void ngtcp2_rst_reset(ngtcp2_rst *rst) { rst->app_limited = 0; rst->is_cwnd_limited = 0; rst->lost = 0; - rst->valid_after_seq = rst->last_seq; } void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, @@ -89,7 +87,6 @@ void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { rs->interval = ngtcp2_max_uint64(rs->send_elapsed, rs->ack_elapsed); rs->delivered = rst->delivered - rs->prior_delivered; - rs->lost = rst->lost - rs->prior_lost; if (rs->interval < cstat->min_rtt) { rs->interval = UINT64_MAX; @@ -103,31 +100,23 @@ void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval; } -static int rst_is_newest_pkt(const ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, - const ngtcp2_rs *rs) { - return ent->ts > rst->first_sent_ts || - (ent->ts == rst->first_sent_ts && ent->rst.end_seq > rs->last_end_seq); +static int is_newest_pkt(const ngtcp2_rtb_entry *ent, const ngtcp2_rs *rs) { + return ent->rst.end_seq > rs->last_end_seq; } void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, ngtcp2_tstamp ts) { ngtcp2_rs *rs = &rst->rs; - if (ent->rst.end_seq <= rst->valid_after_seq) { - return; - } - rst->delivered += ent->pktlen; rst->delivered_ts = ts; - if (rs->prior_ts == UINT64_MAX || rst_is_newest_pkt(rst, ent, rs)) { + if (rs->prior_ts == UINT64_MAX || is_newest_pkt(ent, rs)) { rs->prior_delivered = ent->rst.delivered; rs->prior_ts = ent->rst.delivered_ts; rs->is_app_limited = ent->rst.is_app_limited; rs->send_elapsed = ent->ts - ent->rst.first_sent_ts; rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts; - rs->tx_in_flight = ent->rst.tx_in_flight; - rs->prior_lost = ent->rst.lost; rs->last_end_seq = ent->rst.end_seq; rst->first_sent_ts = ent->ts; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h index c2580306cc59fe..1bb624de79b53f 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h @@ -48,7 +48,6 @@ typedef struct ngtcp2_rs { ngtcp2_tstamp prior_ts; uint64_t tx_in_flight; uint64_t lost; - uint64_t prior_lost; ngtcp2_duration send_elapsed; ngtcp2_duration ack_elapsed; int64_t last_end_seq; @@ -73,10 +72,6 @@ typedef struct ngtcp2_rst { across all packet number spaces, we can replace this with a packet number. */ int64_t last_seq; - /* valid_after_seq is the sequence number, and ignore a packet if - the sequence number of the packet is less than or equal to this - number. */ - int64_t valid_after_seq; int is_cwnd_limited; } ngtcp2_rst; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c index f7a7f5724b7ac3..b50f482bc7e8c0 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -43,16 +43,19 @@ ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent) static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd, ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, size_t pktlen, uint16_t flags) { - memset(ent, 0, sizeof(*ent)); - - ent->hd.pkt_num = hd->pkt_num; - ent->hd.type = hd->type; - ent->hd.flags = hd->flags; - ent->frc = frc; - ent->ts = ts; - ent->lost_ts = UINT64_MAX; - ent->pktlen = pktlen; - ent->flags = flags; + *ent = (ngtcp2_rtb_entry){ + .hd = + { + .pkt_num = hd->pkt_num, + .type = hd->type, + .flags = hd->flags, + }, + .frc = frc, + .ts = ts, + .lost_ts = UINT64_MAX, + .pktlen = pktlen, + .flags = flags, + }; } int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, @@ -102,7 +105,7 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_rst *rst, ngtcp2_cc *cc, rtb->cc_pkt_num = cc_pkt_num; rtb->cc_bytes_in_flight = 0; rtb->num_lost_pkts = 0; - rtb->num_lost_pmtud_pkts = 0; + rtb->num_lost_ignore_pkts = 0; } void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { @@ -153,9 +156,10 @@ static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, assert(rtb->num_lost_pkts); --rtb->num_lost_pkts; - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - assert(rtb->num_lost_pmtud_pkts); - --rtb->num_lost_pmtud_pkts; + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + assert(rtb->num_lost_ignore_pkts); + --rtb->num_lost_ignore_pkts; } return 0; @@ -244,21 +248,16 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags, fr->stream.offset + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); range = ngtcp2_range_intersect(&range, &gap); - if (ngtcp2_range_len(&range) == 0) { - if (!fr->stream.fin) { + if (ngtcp2_range_len(&range) == 0 && !fr->stream.fin && /* 0 length STREAM frame with offset == 0 must be retransmitted if no non-empty data are sent to this stream, fin flag is not set, and no data in this stream are acknowledged. */ - if (fr->stream.offset != 0 || fr->stream.datacnt != 0 || - strm->tx.offset || - (strm->flags & - (NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_ANY_ACKED))) { - continue; - } - } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { - continue; - } + (fr->stream.offset != 0 || fr->stream.datacnt != 0 || + strm->tx.offset || + (strm->flags & + (NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_ANY_ACKED)))) { + continue; } if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) && @@ -443,22 +442,29 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, ngtcp2_cc *cc = rtb->cc; ngtcp2_cc_pkt pkt; - ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, - ent->ts); + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, + ent->ts); - if (rtb->qlog) { - ngtcp2_qlog_pkt_lost(rtb->qlog, ent); + if (rtb->qlog) { + ngtcp2_qlog_pkt_lost(rtb->qlog, ent); + } } - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - ++rtb->num_lost_pmtud_pkts; - } else if (rtb->cc->on_pkt_lost) { - cc->on_pkt_lost(cc, cstat, - ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, - pktns->id, ent->ts, ent->rst.lost, - ent->rst.tx_in_flight, - ent->rst.is_app_limited), - ts); + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + ++rtb->num_lost_ignore_pkts; + } else if (ent->hd.pkt_num >= rtb->cc_pkt_num) { + rtb->rst->lost += ent->pktlen; + + if (rtb->cc->on_pkt_lost) { + cc->on_pkt_lost(cc, cstat, + ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, + pktns->id, ent->ts, ent->rst.lost, + ent->rst.tx_in_flight, + ent->rst.is_app_limited), + ts); + } } if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { @@ -705,6 +711,8 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, ngtcp2_cc *cc = rtb->cc; ngtcp2_cc_pkt pkt; + assert(ent->hd.pkt_num >= rtb->cc_pkt_num); + ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts); if (cc->on_pkt_acked) { @@ -786,7 +794,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, size_t ecn_acked = 0; int verify_ecn = 0; ngtcp2_cc_ack cc_ack = {0}; - size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts; + size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_ignore_pkts; cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight; cc_ack.rtt = UINT64_MAX; @@ -830,6 +838,11 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ent = ngtcp2_ksl_it_get(&it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP) { + rv = NGTCP2_ERR_PROTO; + goto fail; + } + if (largest_ack == pkt_num) { largest_pkt_sent_ts = ent->ts; } @@ -839,7 +852,6 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, } rtb_remove(rtb, &it, &acked_ent, ent, cstat); - ++num_acked; } for (i = 0; i < fr->rangecnt; ++i) { @@ -859,12 +871,16 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ent = ngtcp2_ksl_it_get(&it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP) { + rv = NGTCP2_ERR_PROTO; + goto fail; + } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { ack_eliciting_pkt_acked = 1; } rtb_remove(rtb, &it, &acked_ent, ent, cstat); - ++num_acked; } } @@ -873,7 +889,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ngtcp2_max_uint64(pkt_ts - largest_pkt_sent_ts, NGTCP2_NANOSECONDS); rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts); - if (rv == 0 && cc->new_rtt_sample) { + if (rv == 0 && cc->new_rtt_sample && + rtb->largest_acked_tx_pkt_num >= rtb->cc_pkt_num) { cc->new_rtt_sample(cc, cstat, ts); } } @@ -895,9 +912,12 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, cc_ack.bytes_delivered += ent->pktlen; cc_ack.pkt_delivered = ent->rst.delivered; + + rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); + + ++num_acked; } - rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); acked_ent = ent->next; ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); @@ -912,13 +932,14 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, for (ent = acked_ent; ent; ent = acked_ent) { rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); acked_ent = ent->next; + ++num_acked; ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); } } if (rtb->cc->on_spurious_congestion && num_lost_pkts && - rtb->num_lost_pkts == rtb->num_lost_pmtud_pkts) { + rtb->num_lost_pkts == rtb->num_lost_ignore_pkts) { rtb->cc->on_spurious_congestion(cc, cstat, ts); } @@ -933,8 +954,6 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, } } - rtb->rst->lost += cc_ack.bytes_lost; - cc_ack.largest_pkt_sent_ts = largest_pkt_sent_ts; if (num_acked && cc->on_ack_recv) { cc->on_ack_recv(cc, cstat, &cc_ack, ts); @@ -1189,8 +1208,9 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { --rtb->num_lost_pkts; - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - --rtb->num_lost_pmtud_pkts; + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + --rtb->num_lost_ignore_pkts; } rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); @@ -1200,27 +1220,28 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { } } -void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, + ngtcp2_duration timeout, ngtcp2_tstamp ts) { ngtcp2_ksl_it it; ngtcp2_rtb_entry *ent; int rv; (void)rv; - if (ngtcp2_ksl_len(&rtb->ents) == 0) { + if (rtb->num_lost_pkts == 0) { return; } it = ngtcp2_ksl_end(&rtb->ents); - for (;;) { + for (; rtb->num_lost_pkts;) { assert(ngtcp2_ksl_it_end(&it)); ngtcp2_ksl_it_prev(&it); ent = ngtcp2_ksl_it_get(&it); if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) || - ts - ent->lost_ts < pto) { + ts - ent->lost_ts < timeout) { return; } @@ -1229,18 +1250,15 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, --rtb->num_lost_pkts; - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - --rtb->num_lost_pmtud_pkts; + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + --rtb->num_lost_ignore_pkts; } rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); - - if (ngtcp2_ksl_len(&rtb->ents) == 0) { - return; - } } } @@ -1263,60 +1281,14 @@ ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(const ngtcp2_rtb *rtb) { return ent->lost_ts; } -static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, - ngtcp2_rtb_entry *ent) { - ngtcp2_frame_chain **pfrc, *frc; +static int rtb_reclaim_frame_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_rtb_entry *ent) { + ngtcp2_frame_chain **pfrc = &ent->frc, *frc; ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; - ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, - ent->ts); - - if (rtb->qlog) { - ngtcp2_qlog_pkt_lost(rtb->qlog, ent); - } - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, - "pkn=%" PRId64 - " is a probe packet, no retransmission is necessary", - ent->hd.pkt_num); - return 0; - } - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { - --rtb->num_lost_pkts; - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - --rtb->num_lost_pmtud_pkts; - } - - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, - "pkn=%" PRId64 - " was declared lost and has already been retransmitted", - ent->hd.pkt_num); - return 0; - } - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, - "pkn=%" PRId64 " has already been reclaimed on PTO", - ent->hd.pkt_num); - return 0; - } - - if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && - (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) || - !conn->callbacks.lost_datagram)) { - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ - return 0; - } - - pfrc = &ent->frc; - for (; *pfrc;) { switch ((*pfrc)->fr.type) { case NGTCP2_FRAME_STREAM: @@ -1390,14 +1362,12 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } -int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) { +int ngtcp2_rtb_reclaim_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) { ngtcp2_rtb_entry *ent; - ngtcp2_ksl_it it; + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rtb->ents); int rv; - it = ngtcp2_ksl_begin(&rtb->ents); - for (; !ngtcp2_ksl_it_end(&it);) { ent = ngtcp2_ksl_it_get(&it); @@ -1405,7 +1375,42 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, + ent->hd.flags, ent->ts); + + if (rtb->qlog) { + ngtcp2_qlog_pkt_lost(rtb->qlog, ent); + } + } + + /* We never send PING only probe packet because we should have + CRYPTO data or just nothing. If we have nothing, then we do + not send probe packet. */ + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)); + + /* We never get ACK before Retry packet. */ + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(0 == rtb->num_lost_pkts); + assert(0 == rtb->num_lost_ignore_pkts); + + /* PMTUD probe must not be sent before handshake completion. */ + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE)); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, + "pkn=%" PRId64 " has already been reclaimed on PTO", + ent->hd.pkt_num); + continue; + } + + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && + (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) || + !conn->callbacks.lost_datagram)) { + continue; + } + + rv = rtb_reclaim_frame_on_retry(rtb, conn, pktns, ent); ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h index 3a9397eac5bb6f..14684a458a60e6 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h @@ -80,6 +80,9 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; /* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry includes a packet which elicits PTO probe packets. */ #define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u +/* NGTCP2_RTB_ENTRY_FLAG_SKIP indicates that the entry has the skipped + packet number. */ +#define NGTCP2_RTB_ENTRY_FLAG_SKIP 0x200u typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; @@ -187,10 +190,11 @@ typedef struct ngtcp2_rtb { /* num_lost_pkts is the number entries in ents which has NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */ size_t num_lost_pkts; - /* num_lost_pmtud_pkts is the number of entries in ents which have - both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and - NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */ - size_t num_lost_pmtud_pkts; + /* num_lost_ignore_pkts is the number of entries in ents which have + NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set, and should be + excluded from lost byte count. If only those packets are lost, + congestion event is not triggered. */ + size_t num_lost_ignore_pkts; } ngtcp2_rtb; /* @@ -258,7 +262,8 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, /* * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. */ -void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, + ngtcp2_duration timeout, ngtcp2_tstamp ts); /* @@ -269,12 +274,12 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(const ngtcp2_rtb *rtb); /* - * ngtcp2_rtb_remove_all removes all packets from |rtb|, and prepends - * all frames to |*pfrc|. Even when this function fails, some frames - * might be prepended to |*pfrc|, and the caller should handle them. + * ngtcp2_rtb_reclaim_on_retry is called when Retry packet is + * received. It removes all packets from |rtb|, and retransmittable + * frames are reclaimed for retransmission. */ -int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); +int ngtcp2_rtb_reclaim_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); /* * ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c index 8ea969c4addbdc..70ec6ee2fe9ea7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c @@ -120,7 +120,7 @@ uint64_t ngtcp2_strm_rx_offset(const ngtcp2_strm *strm) { /* strm_rob_heavily_fragmented returns nonzero if the number of gaps in |rob| exceeds the limit. */ static int strm_rob_heavily_fragmented(const ngtcp2_rob *rob) { - return ngtcp2_ksl_len(&rob->gapksl) >= 5000; + return ngtcp2_ksl_len(&rob->gapksl) >= 1000; } int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, @@ -138,11 +138,16 @@ int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, } } + rv = ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); + if (rv != 0) { + return rv; + } + if (strm_rob_heavily_fragmented(strm->rx.rob)) { return NGTCP2_ERR_INTERNAL; } - return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); + return 0; } void ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) { @@ -196,6 +201,8 @@ int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) { if (rv != 0) { return rv; } + } else if (ngtcp2_ksl_len(strm->tx.streamfrq) >= 1000) { + return NGTCP2_ERR_INTERNAL; } return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset, @@ -398,7 +405,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, datalen = ngtcp2_vec_len(fr->data, fr->datacnt); /* datalen could be zero if 0 length STREAM has been sent */ - if (left == 0 && datalen) { + /* We might see more data in the queue, then left < datalen could be + true. We only see the first one for now. */ + if ((fr->type == NGTCP2_FRAME_STREAM && + (left < datalen && left < NGTCP2_MIN_STREAM_DATALEN)) || + (left == 0 && datalen)) { rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); @@ -737,7 +748,16 @@ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) { } } - return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); + rv = ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); + if (rv != 0) { + return rv; + } + + if (ngtcp2_ksl_len(&strm->tx.acked_offset->gap) >= 1000) { + return NGTCP2_ERR_INTERNAL; + } + + return 0; } void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c index ca517532e3a695..d652ece4e07a88 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c @@ -523,12 +523,12 @@ int ngtcp2_transport_params_decode_versioned(int transport_params_version, } /* Set default values */ - memset(params, 0, sizeof(*params)); - params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; - params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; - params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; - params->active_connection_id_limit = - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + *params = (ngtcp2_transport_params){ + .max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE, + .ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT, + .max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY, + .active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, + }; p = end = data; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h index 55e735d164edcb..d90a3204a9f2b4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h @@ -33,13 +33,6 @@ #include "ngtcp2_mem.h" -/* - * ngtcp2_vec_lit is a convenient macro to fill the object pointed by - * |DEST| with the literal string |LIT|. - */ -#define ngtcp2_vec_lit(DEST, LIT) \ - ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST)) - /* * ngtcp2_vec_init initializes |vec| with the given parameters. It * returns |vec|. diff --git a/node.gyp b/node.gyp index 4437a34f9cf67b..26150fa5c5ec03 100644 --- a/node.gyp +++ b/node.gyp @@ -187,6 +187,8 @@ 'src/udp_wrap.cc', 'src/util.cc', 'src/uv.cc', + 'src/quic/cid.cc', + 'src/quic/data.cc', # headers to make for a more pleasant IDE experience 'src/aliased_buffer.h', 'src/aliased_buffer-inl.h', @@ -323,6 +325,10 @@ 'src/udp_wrap.h', 'src/util.h', 'src/util-inl.h', + 'src/quic/cid.h', + 'src/quic/data.h', + 'src/quic/defs.h', + 'src/quic/guard.h', ], 'node_crypto_sources': [ 'src/crypto/crypto_aes.cc', @@ -379,8 +385,6 @@ 'node_quic_sources': [ 'src/quic/application.cc', 'src/quic/bindingdata.cc', - 'src/quic/cid.cc', - 'src/quic/data.cc', 'src/quic/endpoint.cc', 'src/quic/http3.cc', 'src/quic/logstream.cc', @@ -394,8 +398,6 @@ 'src/quic/transportparams.cc', 'src/quic/application.h', 'src/quic/bindingdata.h', - 'src/quic/cid.h', - 'src/quic/data.h', 'src/quic/endpoint.h', 'src/quic/http3.h', 'src/quic/logstream.h', diff --git a/node.gypi b/node.gypi index a851fbbcb810bb..5ce22a3102c82c 100644 --- a/node.gypi +++ b/node.gypi @@ -380,6 +380,8 @@ 'defines': [ 'OPENSSL_API_COMPAT=0x10100000L', ], 'dependencies': [ './deps/openssl/openssl.gyp:openssl', + './deps/ngtcp2/ngtcp2.gyp:ngtcp2', + './deps/ngtcp2/ngtcp2.gyp:nghttp3', # For tests './deps/openssl/openssl.gyp:openssl-cli', diff --git a/src/quic/cid.cc b/src/quic/cid.cc index 1b5fdd861b7a9a..2f72d24c892f43 100644 --- a/src/quic/cid.cc +++ b/src/quic/cid.cc @@ -1,12 +1,14 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC -#include "cid.h" +#if HAVE_OPENSSL +#include "guard.h" +#ifndef OPENSSL_NO_QUIC #include #include #include #include +#include "cid.h" +#include "defs.h" #include "nbytes.h" #include "ncrypto.h" -#include "quic/defs.h" namespace node::quic { @@ -77,7 +79,7 @@ std::string CID::ToString() const { return std::string(dest, written); } -CID CID::kInvalid{}; +const CID CID::kInvalid{}; // ============================================================================ // CID::Hash @@ -95,12 +97,12 @@ size_t CID::Hash::operator()(const CID& cid) const { // CID::Factory namespace { -class RandomCIDFactory : public CID::Factory { +class RandomCIDFactory final : public CID::Factory { public: RandomCIDFactory() = default; DISALLOW_COPY_AND_MOVE(RandomCIDFactory) - CID Generate(size_t length_hint) const override { + const CID Generate(size_t length_hint) const override { DCHECK_GE(length_hint, CID::kMinLength); DCHECK_LE(length_hint, CID::kMaxLength); Mutex::ScopedLock lock(mutex_); @@ -110,8 +112,8 @@ class RandomCIDFactory : public CID::Factory { return CID(start, length_hint); } - CID GenerateInto(ngtcp2_cid* cid, - size_t length_hint = CID::kMaxLength) const override { + const CID GenerateInto(ngtcp2_cid* cid, + size_t length_hint = CID::kMaxLength) const override { DCHECK_GE(length_hint, CID::kMinLength); DCHECK_LE(length_hint, CID::kMaxLength); Mutex::ScopedLock lock(mutex_); @@ -135,7 +137,7 @@ class RandomCIDFactory : public CID::Factory { } } - static constexpr int kPoolSize = 4096; + static constexpr int kPoolSize = 1024 * 16; mutable int pos_ = kPoolSize; mutable uint8_t pool_[kPoolSize]; mutable Mutex mutex_; @@ -148,4 +150,5 @@ const CID::Factory& CID::Factory::random() { } } // namespace node::quic -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSS diff --git a/src/quic/cid.h b/src/quic/cid.h index 15aa6d90748007..16883e50f1637a 100644 --- a/src/quic/cid.h +++ b/src/quic/cid.h @@ -1,7 +1,7 @@ #pragma once #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC + #include #include #include @@ -51,7 +51,7 @@ class CID final : public MemoryRetainer { CID(const CID& other); CID& operator=(const CID& other); - CID(CID&&) = delete; + DISALLOW_MOVE(CID) struct Hash final { size_t operator()(const CID& cid) const; @@ -68,6 +68,8 @@ class CID final : public MemoryRetainer { operator bool() const; size_t length() const; + // Returns a hex-encoded string representation of the CID useful + // for debugging. std::string ToString() const; SET_NO_MEMORY_INFO() @@ -75,7 +77,7 @@ class CID final : public MemoryRetainer { SET_SELF_SIZE(CID) template - using Map = std::unordered_map; + using Map = std::unordered_map; // A CID::Factory, as the name suggests, is used to create new CIDs. // Per https://datatracker.ietf.org/doc/draft-ietf-quic-load-balancers/, QUIC @@ -85,13 +87,13 @@ class CID final : public MemoryRetainer { // but will allow user code to provide their own CID::Factory implementation. class Factory; - static CID kInvalid; + static const CID kInvalid; // The default constructor creates an empty, zero-length CID. // Zero-length CIDs are not usable. We use them as a placeholder // for a missing or empty CID value. This is public only because // it is required for the CID::Map implementation. It should not - // be used. Use kInvalid instead. + // be used directly. Use kInvalid instead. CID(); private: @@ -107,12 +109,12 @@ class CID::Factory { // Generate a new CID. The length_hint must be between CID::kMinLength // and CID::kMaxLength. The implementation can choose to ignore the length. - virtual CID Generate(size_t length_hint = CID::kMaxLength) const = 0; + virtual const CID Generate(size_t length_hint = CID::kMaxLength) const = 0; // Generate a new CID into the given ngtcp2_cid. This variation of // Generate should be used far less commonly. - virtual CID GenerateInto(ngtcp2_cid* cid, - size_t length_hint = CID::kMaxLength) const = 0; + virtual const CID GenerateInto( + ngtcp2_cid* cid, size_t length_hint = CID::kMaxLength) const = 0; // The default random CID generator instance. static const Factory& random(); @@ -123,5 +125,4 @@ class CID::Factory { } // namespace node::quic -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/quic/data.cc b/src/quic/data.cc index 699ddf4b210e8b..d54e4c6af2236c 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -1,5 +1,6 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC - +#if HAVE_OPENSSL +#include "guard.h" +#ifndef OPENSSL_NO_QUIC #include "data.h" #include #include @@ -13,15 +14,21 @@ namespace node { using v8::Array; +using v8::ArrayBuffer; +using v8::ArrayBufferView; +using v8::BackingStore; using v8::BigInt; -using v8::Integer; +using v8::Just; using v8::Local; +using v8::Maybe; using v8::MaybeLocal; +using v8::Nothing; using v8::Uint8Array; using v8::Undefined; using v8::Value; namespace quic { +int DebugIndentScope::indent_ = 0; Path::Path(const SocketAddress& local, const SocketAddress& remote) { ngtcp2_addr_init(&this->local, local.data(), local.length()); @@ -50,9 +57,6 @@ std::string Path::ToString() const { PathStorage::PathStorage() { Reset(); } -PathStorage::operator ngtcp2_path() { - return path; -} void PathStorage::Reset() { ngtcp2_path_storage_zero(this); @@ -72,44 +76,54 @@ bool PathStorage::operator!=(const PathStorage& other) const { // ============================================================================ -Store::Store(std::shared_ptr store, - size_t length, - size_t offset) +Store::Store(std::shared_ptr store, size_t length, size_t offset) : store_(std::move(store)), length_(length), offset_(offset) { CHECK_LE(offset_, store_->ByteLength()); CHECK_LE(length_, store_->ByteLength() - offset_); } -Store::Store(std::unique_ptr store, - size_t length, - size_t offset) +Store::Store(std::unique_ptr store, size_t length, size_t offset) : store_(std::move(store)), length_(length), offset_(offset) { CHECK_LE(offset_, store_->ByteLength()); CHECK_LE(length_, store_->ByteLength() - offset_); } -Store::Store(Local buffer, Option option) - : Store(buffer->GetBackingStore(), buffer->ByteLength()) { - if (option == Option::DETACH) { - USE(buffer->Detach(Local())); +Maybe Store::From( + Local buffer, + Local detach_key) { + if (!buffer->IsDetachable()) { + return Nothing(); + } + bool res; + auto backing = buffer->GetBackingStore(); + auto length = buffer->ByteLength(); + if (!buffer->Detach(detach_key).To(&res) || !res) { + return Nothing(); } + return Just(Store(std::move(backing), length, 0)); } -Store::Store(Local view, Option option) - : Store(view->Buffer()->GetBackingStore(), - view->ByteLength(), - view->ByteOffset()) { - if (option == Option::DETACH) { - USE(view->Buffer()->Detach(Local())); +Maybe Store::From( + Local view, + Local detach_key) { + if (!view->Buffer()->IsDetachable()) { + return Nothing(); } + bool res; + auto backing = view->Buffer()->GetBackingStore(); + auto length = view->ByteLength(); + auto offset = view->ByteOffset(); + if (!view->Buffer()->Detach(detach_key).To(&res) || !res) { + return Nothing(); + } + return Just(Store(std::move(backing), length, offset)); } Local Store::ToUint8Array(Environment* env) const { return !store_ - ? Uint8Array::New(v8::ArrayBuffer::New(env->isolate(), 0), 0, 0) - : Uint8Array::New(v8::ArrayBuffer::New(env->isolate(), store_), - offset_, - length_); + ? Uint8Array::New(ArrayBuffer::New(env->isolate(), 0), 0, 0) + : Uint8Array::New( + ArrayBuffer::New(env->isolate(), store_), offset_, length_); } Store::operator bool() const { @@ -119,25 +133,31 @@ size_t Store::length() const { return length_; } -template +size_t Store::total_length() const { + return store_ ? store_->ByteLength() : 0; +} + +template T Store::convert() const { + // We can only safely convert to T if we have a valid store. + CHECK(store_); T buf; buf.base = - store_ != nullptr ? static_cast(store_->Data()) + offset_ : nullptr; + store_ != nullptr ? static_cast(store_->Data()) + offset_ : nullptr; buf.len = length_; return buf; } Store::operator uv_buf_t() const { - return convert(); + return convert(); } Store::operator ngtcp2_vec() const { - return convert(); + return convert(); } Store::operator nghttp3_vec() const { - return convert(); + return convert(); } void Store::MemoryInfo(MemoryTracker* tracker) const { @@ -147,18 +167,23 @@ void Store::MemoryInfo(MemoryTracker* tracker) const { // ============================================================================ namespace { -std::string TypeName(QuicError::Type type) { +constexpr std::string_view TypeName(QuicError::Type type) { switch (type) { case QuicError::Type::APPLICATION: - return "APPLICATION"; + return "application"; case QuicError::Type::TRANSPORT: - return "TRANSPORT"; + return "transport"; case QuicError::Type::VERSION_NEGOTIATION: - return "VERSION_NEGOTIATION"; + return "version_negotiation"; case QuicError::Type::IDLE_CLOSE: - return "IDLE_CLOSE"; + return "idle_close"; + case QuicError::Type::DROP_CONNECTION: + return "drop_connection"; + case QuicError::Type::RETRY: + return "retry"; + default: + return ""; } - UNREACHABLE(); } } // namespace @@ -167,6 +192,8 @@ QuicError::QuicError(const std::string& reason) ngtcp2_ccerr_default(&error_); } +// Keep in mind that reason_ in each of the constructors here will copy +// the string from the ngtcp2_ccerr input. QuicError::QuicError(const ngtcp2_ccerr* ptr) : reason_(reinterpret_cast(ptr->reason), ptr->reasonlen), error_(), @@ -177,14 +204,6 @@ QuicError::QuicError(const ngtcp2_ccerr& error) error_(error), ptr_(&error_) {} -QuicError::operator bool() const { - if ((code() == QUIC_NO_ERROR && type() == Type::TRANSPORT) || - ((code() == QUIC_APP_NO_ERROR && type() == Type::APPLICATION))) { - return false; - } - return true; -} - const uint8_t* QuicError::reason_c_str() const { return reinterpret_cast(reason_.c_str()); } @@ -247,31 +266,45 @@ error_code QuicError::h3_liberr_to_code(int liberr) { return nghttp3_err_infer_quic_app_error_code(liberr); } -bool QuicError::is_crypto() const { +bool QuicError::is_crypto_error() const { return code() & NGTCP2_CRYPTO_ERROR; } -std::optional QuicError::crypto_error() const { - if (!is_crypto()) return std::nullopt; +std::optional QuicError::get_crypto_error() const { + if (!is_crypto_error()) return std::nullopt; return code() & ~NGTCP2_CRYPTO_ERROR; } MaybeLocal QuicError::ToV8Value(Environment* env) const { if ((type() == Type::TRANSPORT && code() == NGTCP2_NO_ERROR) || - (type() == Type::APPLICATION && code() == NGTCP2_APP_NOERROR) || (type() == Type::APPLICATION && code() == NGHTTP3_H3_NO_ERROR)) { + // Note that we only return undefined for *known* no-error application + // codes. It is possible that other application types use other specific + // no-error codes, but since we don't know which application is being used, + // we'll just return the error code value for those below. return Undefined(env->isolate()); } + Local type_str; + if (!node::ToV8Value(env->context(), TypeName(type())).ToLocal(&type_str)) { + return {}; + } + Local argv[] = { - Integer::New(env->isolate(), static_cast(type())), + type_str, BigInt::NewFromUnsigned(env->isolate(), code()), Undefined(env->isolate()), }; + // Note that per the QUIC specification, the reason, if present, is + // expected to be UTF-8 encoded. The spec uses the term "SHOULD" here, + // which means that is is entirely possible that some QUIC implementation + // could choose a different encoding, in which case the conversion here + // will produce garbage. That's ok though, we're going to use the default + // assumption that the impl is following the guidelines. if (reason_.length() > 0 && !node::ToV8Value(env->context(), reason()).ToLocal(&argv[2])) { - return MaybeLocal(); + return {}; } return Array::New(env->isolate(), argv, arraysize(argv)).As(); @@ -279,7 +312,8 @@ MaybeLocal QuicError::ToV8Value(Environment* env) const { std::string QuicError::ToString() const { std::string str = "QuicError("; - str += TypeName(type()) + ") "; + str += TypeName(type()); + str += ") "; str += std::to_string(code()); if (!reason_.empty()) str += ": " + reason_; return str; @@ -289,53 +323,78 @@ void QuicError::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("reason", reason_.length()); } -QuicError QuicError::ForTransport(error_code code, std::string reason) { +const QuicError QuicError::ForTransport(TransportError code, + std::string reason) { + return ForTransport(static_cast(code), std::move(reason)); +} + +const QuicError QuicError::ForTransport(error_code code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_transport_error( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::ForApplication(error_code code, std::string reason) { +const QuicError QuicError::ForApplication(Http3Error code, std::string reason) { + return ForApplication(static_cast(code), std::move(reason)); +} + +const QuicError QuicError::ForApplication(error_code code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_application_error( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::ForVersionNegotiation(std::string reason) { +const QuicError QuicError::ForVersionNegotiation(std::string reason) { return ForNgtcp2Error(NGTCP2_ERR_RECV_VERSION_NEGOTIATION, std::move(reason)); } -QuicError QuicError::ForIdleClose(std::string reason) { +const QuicError QuicError::ForIdleClose(std::string reason) { return ForNgtcp2Error(NGTCP2_ERR_IDLE_CLOSE, std::move(reason)); } -QuicError QuicError::ForNgtcp2Error(int code, std::string reason) { +const QuicError QuicError::ForDropConnection(std::string reason) { + return ForNgtcp2Error(NGTCP2_ERR_DROP_CONN, std::move(reason)); +} + +const QuicError QuicError::ForRetry(std::string reason) { + return ForNgtcp2Error(NGTCP2_ERR_RETRY, std::move(reason)); +} + +const QuicError QuicError::ForNgtcp2Error(int code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_liberr( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::ForTlsAlert(int code, std::string reason) { +const QuicError QuicError::ForTlsAlert(int code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_tls_alert( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) { +const QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) { return QuicError(ngtcp2_conn_get_ccerr(session)); } -QuicError QuicError::TRANSPORT_NO_ERROR = ForTransport(QUIC_NO_ERROR); -QuicError QuicError::APPLICATION_NO_ERROR = ForApplication(QUIC_APP_NO_ERROR); -QuicError QuicError::VERSION_NEGOTIATION = ForVersionNegotiation(); -QuicError QuicError::IDLE_CLOSE = ForIdleClose(); -QuicError QuicError::INTERNAL_ERROR = ForNgtcp2Error(NGTCP2_ERR_INTERNAL); +#define V(name) \ + const QuicError QuicError::TRANSPORT_##name = \ + ForTransport(TransportError::name); +QUIC_TRANSPORT_ERRORS(V) +#undef V + +const QuicError QuicError::HTTP3_NO_ERROR = ForApplication(NGHTTP3_H3_NO_ERROR); +const QuicError QuicError::VERSION_NEGOTIATION = ForVersionNegotiation(); +const QuicError QuicError::IDLE_CLOSE = ForIdleClose(); +const QuicError QuicError::DROP_CONNECTION = ForDropConnection(); +const QuicError QuicError::RETRY = ForRetry(); +const QuicError QuicError::INTERNAL_ERROR = ForNgtcp2Error(NGTCP2_ERR_INTERNAL); } // namespace quic } // namespace node -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSSL diff --git a/src/quic/data.h b/src/quic/data.h index 7f97403d61f414..a63bc24b63eced 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -1,29 +1,37 @@ #pragma once #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include #include #include #include #include +#include #include #include +#include #include #include "defs.h" namespace node::quic { +template +concept OneByteType = sizeof(T) == 1; + struct Path final : public ngtcp2_path { - Path(const SocketAddress& local, const SocketAddress& remote); - inline operator ngtcp2_path*() { return this; } + explicit Path(const SocketAddress& local, const SocketAddress& remote); + Path(Path&& other) noexcept = default; + Path& operator=(Path&& other) noexcept = default; + DISALLOW_COPY(Path) std::string ToString() const; }; struct PathStorage final : public ngtcp2_path_storage { - PathStorage(); - operator ngtcp2_path(); + explicit PathStorage(); + PathStorage(PathStorage&& other) noexcept = default; + PathStorage& operator=(PathStorage&& other) noexcept = default; + DISALLOW_COPY(PathStorage) void Reset(); void CopyTo(PathStorage* path) const; @@ -32,6 +40,9 @@ struct PathStorage final : public ngtcp2_path_storage { bool operator!=(const PathStorage& other) const; }; +// Store acts as a wrapper around a v8::BackingStore, providing a convenient +// abstraction to map it to various buffer types used in QUIC, HTTP/3, and +// libuv, taking care of the necessary adjustments for length and offset. class Store final : public MemoryRetainer { public: Store() = default; @@ -42,16 +53,30 @@ class Store final : public MemoryRetainer { Store(std::unique_ptr store, size_t length, size_t offset = 0); - - enum class Option { - NONE, - DETACH, - }; - - Store(v8::Local buffer, Option option = Option::NONE); - Store(v8::Local view, Option option = Option::NONE); + Store(Store&& other) noexcept = default; + Store& operator=(Store&& other) noexcept = default; + DISALLOW_COPY(Store) + + // Creates a Store from the contents of an ArrayBuffer, always detaching + // it in the process. An empty Maybe will be returned if the ArrayBuffer + // is not detachable or detaching failed (likely due to a detach key + // mismatch). + static v8::Maybe From( + v8::Local buffer, + v8::Local detach_key = v8::Local()); + + // Creates a Store from the contents of an ArrayBufferView, always detaching + // it in the process. An empty Maybe will be returned if the ArrayBuffer + // is not detachable or detaching failed (likely due to a detach key + // mismatch). + static v8::Maybe From( + v8::Local view, + v8::Local detach_key = v8::Local()); v8::Local ToUint8Array(Environment* env) const; + inline v8::Local ToUint8Array(Realm* realm) const { + return ToUint8Array(realm->env()); + } operator uv_buf_t() const; operator ngtcp2_vec() const; @@ -59,33 +84,124 @@ class Store final : public MemoryRetainer { operator bool() const; size_t length() const; + // Returns the total length of the underlying store, not just the + // length of the view. This is useful for memory accounting. + size_t total_length() const; + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(Store) SET_SELF_SIZE(Store) private: - template + template T convert() const; std::shared_ptr store_; size_t length_ = 0; size_t offset_ = 0; + + // Because Store holds the v8::BackingStore and not the v8::ArrayBuffer, + // etc, the memory held by the Store is not directly, automatically + // associated with a v8::Isolate, and is therefore not accounted for + // as external memory. It is the responsibility of the owner of the + // Store instance to ensure the memory remains accounted for. }; +// Periodically, these need to be updated to match the latest ngtcp2 defs. +#define QUIC_TRANSPORT_ERRORS(V) \ + V(NO_ERROR) \ + V(INTERNAL_ERROR) \ + V(CONNECTION_REFUSED) \ + V(FLOW_CONTROL_ERROR) \ + V(STREAM_LIMIT_ERROR) \ + V(STREAM_STATE_ERROR) \ + V(FINAL_SIZE_ERROR) \ + V(FRAME_ENCODING_ERROR) \ + V(TRANSPORT_PARAMETER_ERROR) \ + V(CONNECTION_ID_LIMIT_ERROR) \ + V(PROTOCOL_VIOLATION) \ + V(INVALID_TOKEN) \ + V(APPLICATION_ERROR) \ + V(CRYPTO_BUFFER_EXCEEDED) \ + V(KEY_UPDATE_ERROR) \ + V(AEAD_LIMIT_REACHED) \ + V(NO_VIABLE_PATH) \ + V(CRYPTO_ERROR) \ + V(VERSION_NEGOTIATION_ERROR) + +// Periodically, these need to be updated to match the latest nghttp3 defs. +#define HTTP3_APPLICATION_ERRORS(V) \ + V(H3_NO_ERROR) \ + V(H3_GENERAL_PROTOCOL_ERROR) \ + V(H3_INTERNAL_ERROR) \ + V(H3_STREAM_CREATION_ERROR) \ + V(H3_CLOSED_CRITICAL_STREAM) \ + V(H3_FRAME_UNEXPECTED) \ + V(H3_FRAME_ERROR) \ + V(H3_EXCESSIVE_LOAD) \ + V(H3_ID_ERROR) \ + V(H3_SETTINGS_ERROR) \ + V(H3_MISSING_SETTINGS) \ + V(H3_REQUEST_REJECTED) \ + V(H3_REQUEST_CANCELLED) \ + V(H3_REQUEST_INCOMPLETE) \ + V(H3_MESSAGE_ERROR) \ + V(H3_CONNECT_ERROR) \ + V(H3_VERSION_FALLBACK) \ + V(QPACK_DECOMPRESSION_FAILED) \ + V(QPACK_ENCODER_STREAM_ERROR) \ + V(QPACK_DECODER_STREAM_ERROR) + class QuicError final : public MemoryRetainer { public: - static constexpr error_code QUIC_NO_ERROR = NGTCP2_NO_ERROR; - static constexpr error_code QUIC_APP_NO_ERROR = 65280; + // The known error codes for the transport namespace. + enum class TransportError : error_code { +#define V(name) name = NGTCP2_##name, + QUIC_TRANSPORT_ERRORS(V) +#undef V + }; + + // Every QUIC application defines its own error codes in the application + // namespace. These are managed independently of each other and may overlap + // with other applications and even the transport namespace. The only way + // to correctly interpret an application error code is to know which + // application is being used. For convenience, we define constants for the + // known HTTP/3 application error codes here. + enum class Http3Error : error_code { +#define V(name) name = NGHTTP3_##name, + HTTP3_APPLICATION_ERRORS(V) +#undef V + }; + static constexpr error_code QUIC_NO_ERROR = NGTCP2_NO_ERROR; + static constexpr error_code HTTP3_NO_ERROR_CODE = NGHTTP3_H3_NO_ERROR; + + // In QUIC, Errors are represented as namespaced 64-bit error codes. + // The error code only has meaning within the context of a specific + // namespace. The QuicError::Type enum defines the available namespaces. + // There are essentially two namespaces: transport and application, with + // a few additional special-cases that are variants of the transport + // namespace. enum class Type { TRANSPORT = NGTCP2_CCERR_TYPE_TRANSPORT, APPLICATION = NGTCP2_CCERR_TYPE_APPLICATION, + + // These are special cases of transport errors. VERSION_NEGOTIATION = NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION, IDLE_CLOSE = NGTCP2_CCERR_TYPE_IDLE_CLOSE, + DROP_CONNECTION = NGTCP2_CCERR_TYPE_DROP_CONN, + RETRY = NGTCP2_CCERR_TYPE_RETRY, }; + // Do not use the constructors directly in regular use. Use the static + // factory methods instead. Those will ensure that the underlying + // ngtcp2_ccerr is initialized correctly based on the type of error + // being created. explicit QuicError(const std::string& reason = ""); explicit QuicError(const ngtcp2_ccerr* ptr); explicit QuicError(const ngtcp2_ccerr& error); + QuicError(QuicError&& other) noexcept = default; + QuicError& operator=(QuicError&& other) noexcept = default; + DISALLOW_COPY(QuicError) Type type() const; error_code code() const; @@ -95,13 +211,16 @@ class QuicError final : public MemoryRetainer { operator const ngtcp2_ccerr&() const; operator const ngtcp2_ccerr*() const; - // Returns false if the QuicError uses a no_error code with type - // transport or application. - operator bool() const; - - bool is_crypto() const; - std::optional crypto_error() const; + // Crypto errors are a subset of transport errors. The error code includes + // the TLS alert code embedded within it. + bool is_crypto_error() const; + std::optional get_crypto_error() const; + // Note that since application errors are application-specific and we + // don't know which application is being used here, it is possible that + // the comparing two different QuicError instances from different applications + // will return true even if they are not semantically equivalent. This should + // not be a problem in practice. bool operator==(const QuicError& other) const; bool operator!=(const QuicError& other) const; @@ -110,29 +229,59 @@ class QuicError final : public MemoryRetainer { SET_SELF_SIZE(QuicError) std::string ToString() const; + + // Returns an array containing [type, code, reason], where type is a string + // representation of the error type, code is a bigint representation of the + // error code, and reason is a string representation of the error reason, or + // undefined if the reason is the empty string. This is expected to be used + // to construct a JS error object from the information in JS. v8::MaybeLocal ToV8Value(Environment* env) const; + inline v8::MaybeLocal ToV8Value(Realm* realm) const { + return ToV8Value(realm->env()); + } + // Utility functions for getting the default error reason strings for + // internal error codes returned by the underlying ngtcp2/nghttp3 libraries. static std::string reason_for_liberr(int liberr); static std::string reason_for_h3_liberr(int liberr); + + // Utility functions for checking if the given internal error codes are + // considered to be fatal by the underlying ngtcp2/nghttp3 libraries. static bool is_fatal_liberror(int liberr); static bool is_fatal_h3_liberror(int liberr); + + // Utility functions for converting between ngtcp2/nghttp3 internal error + // codes to the corresponding QUIC error codes. static error_code liberr_to_code(int liberr); static error_code h3_liberr_to_code(int liberr); - static QuicError ForTransport(error_code code, std::string reason = ""); - static QuicError ForApplication(error_code code, std::string reason = ""); - static QuicError ForVersionNegotiation(std::string reason = ""); - static QuicError ForIdleClose(std::string reason = ""); - static QuicError ForNgtcp2Error(int code, std::string reason = ""); - static QuicError ForTlsAlert(int code, std::string reason = ""); - - static QuicError FromConnectionClose(ngtcp2_conn* session); - - static QuicError TRANSPORT_NO_ERROR; - static QuicError APPLICATION_NO_ERROR; - static QuicError VERSION_NEGOTIATION; - static QuicError IDLE_CLOSE; - static QuicError INTERNAL_ERROR; + // Utility functions for creating QuicError instances. + // The reason is expected to always be UTF-8 encoded. + static const QuicError ForTransport(TransportError code, + std::string reason = ""); + static const QuicError ForTransport(error_code code, std::string reason = ""); + static const QuicError ForApplication(Http3Error code, + std::string reason = ""); + static const QuicError ForApplication(error_code code, + std::string reason = ""); + static const QuicError ForVersionNegotiation(std::string reason = ""); + static const QuicError ForIdleClose(std::string reason = ""); + static const QuicError ForDropConnection(std::string reason = ""); + static const QuicError ForRetry(std::string reason = ""); + static const QuicError ForNgtcp2Error(int code, std::string reason = ""); + static const QuicError ForTlsAlert(int code, std::string reason = ""); + + static const QuicError FromConnectionClose(ngtcp2_conn* session); + +#define V(name) static const QuicError TRANSPORT_##name; + QUIC_TRANSPORT_ERRORS(V) +#undef V + static const QuicError HTTP3_NO_ERROR; + static const QuicError VERSION_NEGOTIATION; + static const QuicError IDLE_CLOSE; + static const QuicError DROP_CONNECTION; + static const QuicError RETRY; + static const QuicError INTERNAL_ERROR; private: const uint8_t* reason_c_str() const; @@ -142,7 +291,17 @@ class QuicError final : public MemoryRetainer { const ngtcp2_ccerr* ptr_ = nullptr; }; +// Marked maybe_unused because these are used in the tests but not in the +// production code. +[[maybe_unused]] static bool operator==(const QuicError::TransportError& lhs, + error_code rhs) { + return static_cast(lhs) == rhs; +} +[[maybe_unused]] static bool operator==(const QuicError::Http3Error& lhs, + error_code rhs) { + return static_cast(lhs) == rhs; +} + } // namespace node::quic -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/quic/defs.h b/src/quic/defs.h index 8c97d30d26f77f..71b9059698a6e8 100644 --- a/src/quic/defs.h +++ b/src/quic/defs.h @@ -221,7 +221,6 @@ enum class DatagramStatus : uint8_t { CC_ALGOS(V) #undef V -constexpr uint64_t NGTCP2_APP_NOERROR = 65280; constexpr size_t kDefaultMaxPacketLength = NGTCP2_MAX_UDP_PAYLOAD_SIZE; constexpr size_t kMaxSizeT = std::numeric_limits::max(); constexpr uint64_t kMaxSafeJsInteger = 9007199254740991; @@ -230,7 +229,7 @@ constexpr size_t kMaxVectorCount = 16; using error_code = uint64_t; -class DebugIndentScope { +class DebugIndentScope final { public: inline DebugIndentScope() { ++indent_; } DISALLOW_COPY_AND_MOVE(DebugIndentScope) diff --git a/src/quic/guard.h b/src/quic/guard.h new file mode 100644 index 00000000000000..c0635034522b16 --- /dev/null +++ b/src/quic/guard.h @@ -0,0 +1,5 @@ +#pragma once + +#ifndef QUIC_NGTCP2_USE_OPENSSL_3_5 +#define OPENSSL_NO_QUIC = 1 +#endif diff --git a/src/quic/quic.cc b/src/quic/quic.cc index f642a725263cef..cb972df2b6c0f8 100644 --- a/src/quic/quic.cc +++ b/src/quic/quic.cc @@ -20,8 +20,6 @@ using v8::Value; namespace quic { -int DebugIndentScope::indent_ = 0; - void CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Endpoint::InitPerIsolate(isolate_data, target); diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc index 6d8a19431f714f..50be7d0fb5348c 100644 --- a/test/cctest/test_quic_cid.cc +++ b/test/cctest/test_quic_cid.cc @@ -1,4 +1,6 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#if HAVE_OPENSSL +#include "quic/guard.h" +#ifndef OPENSSL_NO_QUIC #include #include #include @@ -12,42 +14,51 @@ using node::quic::CID; TEST(CID, Basic) { auto& random = CID::Factory::random(); { - auto cid = random.Generate(); + // Check that the kInvalid CID is indeed invalid. + CHECK(!CID::kInvalid); + } + { + const auto cid = random.Generate(); CHECK_EQ(cid.length(), CID::kMaxLength); CHECK(cid); CHECK_EQ(cid, cid); } { - auto cid = random.Generate(5); + const auto cid = random.Generate(5); CHECK_EQ(cid.length(), 5); CHECK(cid); } { - auto cid1 = random.Generate(); - auto cid2 = random.Generate(); + const auto cid1 = random.Generate(); + const auto cid2 = random.Generate(); CHECK_NE(cid1, cid2); } { - auto cid1 = random.Generate(5); - auto cid2 = random.Generate(); + const auto cid1 = random.Generate(5); + const auto cid2 = random.Generate(); CHECK_NE(cid1, cid2); } { - auto cid1 = random.Generate(); - auto cid2 = random.Generate(5); + const auto cid1 = random.Generate(); + const auto cid2 = random.Generate(5); CHECK_NE(cid1, cid2); } { - auto cid = CID::kInvalid; // They are copy constructible... - auto cid2 = cid; + CID cid = CID::kInvalid; CHECK(!cid); CHECK_EQ(cid.length(), 0); - CHECK_EQ(cid, cid2); + CHECK_EQ(CID::kInvalid, cid); + + const auto cid2 = random.Generate(); + const auto cid3 = cid2; + CHECK_EQ(cid2, cid3); } { - auto cid1 = random.Generate(); - auto cid2 = random.Generate(); + const auto cid1 = random.Generate(); + const auto cid2 = random.Generate(); + const auto cid3 = random.Generate(); + CID::Map map; map[cid1] = "hello"; map[cid2] = "there"; @@ -55,12 +66,23 @@ TEST(CID, Basic) { CHECK_EQ(map[cid2], "there"); CHECK_NE(map[cid2], "hello"); CHECK_NE(map[cid1], "there"); + + // Remove the two CIDs. + CHECK_EQ(map.erase(cid1), 1); + CHECK_EQ(map.erase(cid2), 1); + + // Make sure they were removed. + CHECK_EQ(map.find(cid1), map.end()); + CHECK_EQ(map.find(cid2), map.end()); + + // The cid3 is not in the map, so it should not be found. + CHECK_EQ(map.find(cid3), map.end()); } { ngtcp2_cid cid_; uint8_t data[] = {1, 2, 3, 4, 5}; ngtcp2_cid_init(&cid_, data, 5); - auto cid = CID(cid_); + const CID cid(cid_); // This variation of the constructor copies the cid_, so if we // modify the original data it doesn't change in the CID. cid_.data[0] = 9; @@ -71,26 +93,28 @@ TEST(CID, Basic) { ngtcp2_cid cid_; uint8_t data[] = {1, 2, 3, 4, 5}; ngtcp2_cid_init(&cid_, data, 5); - auto cid = CID(&cid_); + const CID cid(&cid_); // This variation of the constructor wraps the cid_, so if we - // modify the original data it does change in the CID. + // modify the original data it does change in the CID... not + // that this is something we would normally do. cid_.data[0] = 9; CHECK_EQ(cid.length(), 5); CHECK_EQ(cid.ToString(), "0902030405"); } { // Generate a bunch to ensure that the pool is regenerated. - for (int n = 0; n < 1000; n++) { - random.Generate(); + for (int n = 0; n < 5000; n++) { + CHECK(random.Generate()); } } { ngtcp2_cid cid_; // Generate a bunch to ensure that the pool is regenerated. - for (int n = 0; n < 1000; n++) { - random.GenerateInto(&cid_, 10); + for (int n = 0; n < 5000; n++) { + CHECK(random.GenerateInto(&cid_, 10)); CHECK_EQ(cid_.datalen, 10); } } } -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSSL diff --git a/test/cctest/test_quic_error.cc b/test/cctest/test_quic_error.cc index f342f8b9ebf060..04e3524ff611ac 100644 --- a/test/cctest/test_quic_error.cc +++ b/test/cctest/test_quic_error.cc @@ -1,4 +1,6 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#if HAVE_OPENSSL +#include "quic/guard.h" +#ifndef OPENSSL_NO_QUIC #include #include #include @@ -13,7 +15,9 @@ TEST(QuicError, NoError) { CHECK_EQ(err.type(), QuicError::Type::TRANSPORT); CHECK_EQ(err.reason(), ""); CHECK_EQ(err, QuicError::TRANSPORT_NO_ERROR); - CHECK(!err); + + CHECK_EQ(QuicError::TransportError::NO_ERROR, QuicError::QUIC_NO_ERROR); + CHECK_EQ(QuicError::Http3Error::H3_NO_ERROR, QuicError::HTTP3_NO_ERROR_CODE); QuicError err2("a reason"); CHECK_EQ(err2.code(), QuicError::QUIC_NO_ERROR); @@ -29,17 +33,13 @@ TEST(QuicError, NoError) { CHECK_EQ(err3.reason(), ""); CHECK_EQ(err3, QuicError::TRANSPORT_NO_ERROR); - // QuicError's are copy assignable - auto err4 = err3; - CHECK_EQ(err4, err3); - // QuicError's are movable - auto err5 = std::move(err4); + auto err5 = std::move(err3); CHECK_EQ(err5, err3); // Equality check ignores the reason CHECK(err5 == err2); - CHECK(err5 != QuicError::APPLICATION_NO_ERROR); + CHECK(err5 != QuicError::HTTP3_NO_ERROR); const ngtcp2_ccerr& ccerr = err5; CHECK_EQ(ccerr.error_code, NGTCP2_NO_ERROR); @@ -57,8 +57,8 @@ TEST(QuicError, NoError) { QuicError err7(ccerr2); CHECK_EQ(err6, err7); - CHECK_EQ(err.ToString(), "QuicError(TRANSPORT) 0"); - CHECK_EQ(err2.ToString(), "QuicError(TRANSPORT) 0: a reason"); + CHECK_EQ(err.ToString(), "QuicError(transport) 0"); + CHECK_EQ(err2.ToString(), "QuicError(transport) 0: a reason"); ngtcp2_ccerr ccerr3; ngtcp2_ccerr_default(&ccerr3); @@ -68,18 +68,16 @@ TEST(QuicError, NoError) { } TEST(QuicError, ApplicationNoError) { - CHECK_EQ(QuicError::APPLICATION_NO_ERROR.code(), - QuicError::QUIC_APP_NO_ERROR); - CHECK_EQ(QuicError::APPLICATION_NO_ERROR.type(), - QuicError::Type::APPLICATION); - CHECK_EQ(QuicError::APPLICATION_NO_ERROR.reason(), ""); + CHECK_EQ(QuicError::HTTP3_NO_ERROR.code(), QuicError::HTTP3_NO_ERROR_CODE); + CHECK_EQ(QuicError::HTTP3_NO_ERROR.type(), QuicError::Type::APPLICATION); + CHECK_EQ(QuicError::HTTP3_NO_ERROR.reason(), ""); auto err = - QuicError::ForApplication(QuicError::QUIC_APP_NO_ERROR, "a reason"); - CHECK_EQ(err.code(), QuicError::QUIC_APP_NO_ERROR); + QuicError::ForApplication(QuicError::HTTP3_NO_ERROR_CODE, "a reason"); + CHECK_EQ(err.code(), QuicError::HTTP3_NO_ERROR_CODE); CHECK_EQ(err.type(), QuicError::Type::APPLICATION); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(APPLICATION) 65280: a reason"); + CHECK_EQ(err.ToString(), "QuicError(application) 256: a reason"); } TEST(QuicError, VersionNegotiation) { @@ -92,7 +90,7 @@ TEST(QuicError, VersionNegotiation) { CHECK_EQ(err.code(), 0); CHECK_EQ(err.type(), QuicError::Type::VERSION_NEGOTIATION); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(VERSION_NEGOTIATION) 0: a reason"); + CHECK_EQ(err.ToString(), "QuicError(version_negotiation) 0: a reason"); } TEST(QuicError, IdleClose) { @@ -104,7 +102,7 @@ TEST(QuicError, IdleClose) { CHECK_EQ(err.code(), 0); CHECK_EQ(err.type(), QuicError::Type::IDLE_CLOSE); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(IDLE_CLOSE) 0: a reason"); + CHECK_EQ(err.ToString(), "QuicError(idle_close) 0: a reason"); CHECK_EQ(QuicError::IDLE_CLOSE, err); } @@ -114,7 +112,7 @@ TEST(QuicError, InternalError) { CHECK_EQ(err.code(), NGTCP2_INTERNAL_ERROR); CHECK_EQ(err.type(), QuicError::Type::TRANSPORT); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(TRANSPORT) 1: a reason"); + CHECK_EQ(err.ToString(), "QuicError(transport) 1: a reason"); printf("%s\n", QuicError::INTERNAL_ERROR.ToString().c_str()); CHECK_EQ(err, QuicError::INTERNAL_ERROR); @@ -125,8 +123,9 @@ TEST(QuicError, TlsAlert) { CHECK_EQ(err.code(), 257); CHECK_EQ(err.type(), QuicError::Type::TRANSPORT); CHECK_EQ(err.reason(), "a reason"); - CHECK(err.is_crypto()); - CHECK_EQ(err.crypto_error(), 1); + CHECK(err.is_crypto_error()); + CHECK_EQ(err.get_crypto_error(), 1); } -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSSL