diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt index fda8dcb7fc7f2a..98e3dbe7682fb0 100644 --- a/deps/nghttp2/lib/CMakeLists.txt +++ b/deps/nghttp2/lib/CMakeLists.txt @@ -47,7 +47,29 @@ if(WIN32) set(NGHTTP2_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc) endif() -set(EXPORT_SET "${PROJECT_NAME}-targets") +set(NGHTTP2_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(NGHTTP2_VERSION_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") +set(NGHTTP2_PROJECT_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}Config.cmake") +set(NGHTTP2_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") +set(NGHTTP2_CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") +set(NGHTTP2_NAMESPACE "${PROJECT_NAME}::") +set(NGHTTP2_VERSION ${PROJECT_VERSION}) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${NGHTTP2_VERSION_CONFIG}" VERSION ${NGHTTP2_VERSION} COMPATIBILITY SameMajorVersion +) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.cmake.in" "${NGHTTP2_PROJECT_CONFIG}" @ONLY) + +# Install cmake config files +install( + FILES "${NGHTTP2_PROJECT_CONFIG}" "${NGHTTP2_VERSION_CONFIG}" + DESTINATION "${NGHTTP2_CONFIG_INSTALL_DIR}") + +install( + EXPORT "${NGHTTP2_TARGETS_EXPORT_NAME}" + NAMESPACE "${NGHTTP2_NAMESPACE}" + DESTINATION "${NGHTTP2_CONFIG_INSTALL_DIR}") # Public shared library if(BUILD_SHARED_LIBS) @@ -65,7 +87,11 @@ if(BUILD_SHARED_LIBS) $ ) - install(TARGETS ${SHARED_LIB} EXPORT ${EXPORT_SET}) + install(TARGETS ${SHARED_LIB} + EXPORT ${NGHTTP2_TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") list(APPEND nghttp2_exports ${SHARED_LIB}) endif() @@ -87,7 +113,9 @@ if(BUILD_STATIC_LIBS) target_compile_definitions(${STATIC_LIB} PUBLIC "-DNGHTTP2_STATICLIB") - install(TARGETS ${STATIC_LIB} EXPORT ${EXPORT_SET}) + install(TARGETS ${STATIC_LIB} + EXPORT ${NGHTTP2_TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") list(APPEND nghttp2_exports ${STATIC_LIB}) endif() @@ -97,11 +125,7 @@ else() set(LIB_SELECTED ${STATIC_LIB}) endif() -add_library(${PROJECT_NAME}::nghttp2 ALIAS ${LIB_SELECTED}) +add_library(nghttp2 ALIAS ${LIB_SELECTED}) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") - -install(EXPORT ${EXPORT_SET} - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} - NAMESPACE ${PROJECT_NAME}::) diff --git a/deps/nghttp2/lib/Makefile.am b/deps/nghttp2/lib/Makefile.am index 1168c1e6135661..3a743df6ae72e5 100644 --- a/deps/nghttp2/lib/Makefile.am +++ b/deps/nghttp2/lib/Makefile.am @@ -22,7 +22,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = includes -EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in +EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in config.cmake.in AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG) AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \ diff --git a/deps/nghttp2/lib/Makefile.in b/deps/nghttp2/lib/Makefile.in index 7a4cb5e97404b6..bb458a182c9f6d 100644 --- a/deps/nghttp2/lib/Makefile.in +++ b/deps/nghttp2/lib/Makefile.in @@ -360,6 +360,8 @@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ +LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ +LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ @@ -494,7 +496,7 @@ top_srcdir = @top_srcdir@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = includes -EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in +EXTRA_DIST = Makefile.msvc CMakeLists.txt version.rc.in config.cmake.in AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG) AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP2 \ @DEFS@ diff --git a/deps/nghttp2/lib/config.cmake.in b/deps/nghttp2/lib/config.cmake.in new file mode 100644 index 00000000000000..0b677718f462d6 --- /dev/null +++ b/deps/nghttp2/lib/config.cmake.in @@ -0,0 +1,3 @@ +include(CMakeFindDependencyMacro) + +include("${CMAKE_CURRENT_LIST_DIR}/@NGHTTP2_TARGETS_EXPORT_NAME@.cmake") diff --git a/deps/nghttp2/lib/includes/Makefile.in b/deps/nghttp2/lib/includes/Makefile.in index 7504f8e7c75b52..a4687390385ce6 100644 --- a/deps/nghttp2/lib/includes/Makefile.in +++ b/deps/nghttp2/lib/includes/Makefile.in @@ -265,6 +265,8 @@ LIBNGHTTP3_LIBS = @LIBNGHTTP3_LIBS@ LIBNGTCP2_CFLAGS = @LIBNGTCP2_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS = @LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ LIBNGTCP2_CRYPTO_BORINGSSL_LIBS = @LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ +LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS = @LIBNGTCP2_CRYPTO_LIBRESSL_CFLAGS@ +LIBNGTCP2_CRYPTO_LIBRESSL_LIBS = @LIBNGTCP2_CRYPTO_LIBRESSL_LIBS@ LIBNGTCP2_CRYPTO_OSSL_CFLAGS = @LIBNGTCP2_CRYPTO_OSSL_CFLAGS@ LIBNGTCP2_CRYPTO_OSSL_LIBS = @LIBNGTCP2_CRYPTO_OSSL_LIBS@ LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS = @LIBNGTCP2_CRYPTO_QUICTLS_CFLAGS@ diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h index 3d91af55109cc8..19c1874f23a3ca 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h @@ -2039,18 +2039,19 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, /** * @functypedef * - * Callback function invoked when a invalid header name/value pair is + * Callback function invoked when an invalid header name/value pair is * received for the |frame|. * * The parameter and behaviour are similar to * :type:`nghttp2_on_header_callback`. The difference is that this - * callback is only invoked when a invalid header name/value pair is - * received which is treated as stream error if this callback is not - * set. Only invalid regular header field are passed to this - * callback. In other words, invalid pseudo header field is not - * passed to this callback. Also header fields which includes upper - * cased latter are also treated as error without passing them to this - * callback. + * callback is only invoked when an invalid header name/value pair is + * received which is treated as stream error if this callback returns + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` and + * :type:`nghttp2_on_invalid_header_callback2` is not set. Only + * invalid regular header field are passed to this callback. In other + * words, invalid pseudo header field is not passed to this callback. + * Also header fields which includes upper cased latter are also + * treated as error without passing them to this callback. * * This callback is only considered if HTTP messaging validation is * turned on (which is on by default, see @@ -2076,17 +2077,18 @@ typedef int (*nghttp2_on_invalid_header_callback)( /** * @functypedef * - * Callback function invoked when a invalid header name/value pair is + * Callback function invoked when an invalid header name/value pair is * received for the |frame|. * * The parameter and behaviour are similar to * :type:`nghttp2_on_header_callback2`. The difference is that this - * callback is only invoked when a invalid header name/value pair is - * received which is silently ignored if this callback is not set. - * Only invalid regular header field are passed to this callback. In - * other words, invalid pseudo header field is not passed to this - * callback. Also header fields which includes upper cased latter are - * also treated as error without passing them to this callback. + * callback is only invoked when an invalid header name/value pair is + * received which is silently ignored if neither this callback nor + * :type:`nghttp2_on_invalid_header_callback` is set. Only invalid + * regular header field are passed to this callback. In other words, + * invalid pseudo header field is not passed to this callback. Also + * header fields which includes upper cased latter are also treated as + * error without passing them to this callback. * * This callback is only considered if HTTP messaging validation is * turned on (which is on by default, see @@ -2445,6 +2447,15 @@ typedef int (*nghttp2_error_callback2)(nghttp2_session *session, int lib_error_code, const char *msg, size_t len, void *user_data); +/** + * @functypedef + * + * Callback function 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 (*nghttp2_rand_callback)(uint8_t *dest, size_t destlen); + struct nghttp2_session_callbacks; /** @@ -2649,7 +2660,7 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( /** * @function * - * Sets callback function invoked when a invalid header name/value + * Sets callback function invoked when an invalid header name/value * pair is received. If both * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are @@ -2662,7 +2673,7 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( /** * @function * - * Sets callback function invoked when a invalid header name/value + * Sets callback function invoked when an invalid header name/value * pair is received. */ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( @@ -2833,6 +2844,18 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); +/** + * @function + * + * Sets callback function invoked when unpredictable data is needed. + * Although this callback is optional due to the backward + * compatibility, it is recommended to specify it to harden the + * runtime behavior against suspicious activities of a remote + * endpoint. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_rand_callback( + nghttp2_session_callbacks *cbs, nghttp2_rand_callback rand_callback); + /** * @functypedef * @@ -3218,6 +3241,23 @@ nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, NGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option, size_t val); +/** + * @function + * + * This function sets the rate limit for the "glitches", the + * suspicious activities from a remote endpoint. It is a token-bucket + * based rate limiter. |burst| specifies the number of tokens that is + * initially available. The maximum number of tokens is capped to + * this value. |rate| specifies the number of tokens that are + * regenerated per second. When a suspicious activity is detected, + * some amount of tokens are consumed. If there is no token + * available, GOAWAY is sent to tear down the connection. |burst| and + * |rate| default to 1000 and 33 respectively. + */ +NGHTTP2_EXTERN void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, + uint64_t burst, + uint64_t rate); + /** * @function * diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h index 776c4924276b85..bd5e6c28723232 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h @@ -29,7 +29,7 @@ * @macro * Version number of the nghttp2 library release */ -#define NGHTTP2_VERSION "1.66.0" +#define NGHTTP2_VERSION "1.67.1" /** * @macro @@ -37,6 +37,6 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define NGHTTP2_VERSION_NUM 0x014200 +#define NGHTTP2_VERSION_NUM 0x014301 #endif /* NGHTTP2VER_H */ diff --git a/deps/nghttp2/lib/nghttp2_callbacks.c b/deps/nghttp2/lib/nghttp2_callbacks.c index 32fedd52d85a3f..162ca6bd4b5332 100644 --- a/deps/nghttp2/lib/nghttp2_callbacks.c +++ b/deps/nghttp2/lib/nghttp2_callbacks.c @@ -201,3 +201,8 @@ void nghttp2_session_callbacks_set_error_callback2( nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) { cbs->error_callback2 = error_callback2; } + +void nghttp2_session_callbacks_set_rand_callback( + nghttp2_session_callbacks *cbs, nghttp2_rand_callback rand_callback) { + cbs->rand_callback = rand_callback; +} diff --git a/deps/nghttp2/lib/nghttp2_callbacks.h b/deps/nghttp2/lib/nghttp2_callbacks.h index a611f485481e7c..ad515970c48a36 100644 --- a/deps/nghttp2/lib/nghttp2_callbacks.h +++ b/deps/nghttp2/lib/nghttp2_callbacks.h @@ -151,6 +151,7 @@ struct nghttp2_session_callbacks { nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback; nghttp2_error_callback error_callback; nghttp2_error_callback2 error_callback2; + nghttp2_rand_callback rand_callback; }; #endif /* NGHTTP2_CALLBACKS_H */ diff --git a/deps/nghttp2/lib/nghttp2_map.c b/deps/nghttp2/lib/nghttp2_map.c index ee6bb1967ec6da..f89f310319a1a3 100644 --- a/deps/nghttp2/lib/nghttp2_map.c +++ b/deps/nghttp2/lib/nghttp2_map.c @@ -31,12 +31,13 @@ #include "nghttp2_helper.h" -#define NGHTTP2_INITIAL_TABLE_LENBITS 4 +#define NGHTTP2_INITIAL_HASHBITS 4 -void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { +void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem) { map->mem = mem; map->hashbits = 0; map->table = NULL; + map->seed = seed; map->size = 0; } @@ -77,8 +78,13 @@ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr), return 0; } -static size_t hash(nghttp2_map_key_type key, size_t bits) { - return (size_t)(((uint32_t)key * 2654435769u) >> (32 - bits)); +static size_t map_hash(const nghttp2_map *map, nghttp2_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. */ + uint32_t h = ((uint32_t)key + map->seed) * 0x93d765dd; + return (size_t)((h * 2654435769u) >> (32 - map->hashbits)); } static void map_bucket_swap(nghttp2_map_bucket *a, nghttp2_map_bucket *b) { @@ -109,24 +115,28 @@ void nghttp2_map_print_distance(const nghttp2_map *map) { continue; } - idx = hash(bkt->key, map->hashbits); + idx = map_hash(map, bkt->key); fprintf(stderr, "@%zu hash=%zu key=%d 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 /* !WIN32 */ - -static int insert(nghttp2_map_bucket *table, size_t hashbits, - nghttp2_map_key_type key, void *data) { - size_t idx = hash(key, hashbits); - nghttp2_map_bucket b = {0, key, data}, *bkt; - size_t mask = (1u << hashbits) - 1; +#endif /* !defined(WIN32) */ + +static int map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { + size_t idx = map_hash(map, key); + nghttp2_map_bucket b = { + .key = key, + .data = data, + }; + nghttp2_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; } @@ -147,15 +157,19 @@ static int insert(nghttp2_map_bucket *table, size_t hashbits, static int map_resize(nghttp2_map *map, size_t new_hashbits) { size_t i; - nghttp2_map_bucket *new_table; nghttp2_map_bucket *bkt; size_t tablelen; int rv; + nghttp2_map new_map = { + .table = nghttp2_mem_calloc(map->mem, 1u << new_hashbits, + sizeof(nghttp2_map_bucket)), + .mem = map->mem, + .seed = map->seed, + .hashbits = new_hashbits, + }; (void)rv; - new_table = nghttp2_mem_calloc(map->mem, 1u << new_hashbits, - sizeof(nghttp2_map_bucket)); - if (new_table == NULL) { + if (new_map.table == NULL) { return NGHTTP2_ERR_NOMEM; } @@ -168,15 +182,15 @@ static int map_resize(nghttp2_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); } } nghttp2_mem_free(map->mem, map->table); + map->table = new_map.table; map->hashbits = new_hashbits; - map->table = new_table; return 0; } @@ -186,30 +200,28 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_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, NGHTTP2_INITIAL_TABLE_LENBITS); + rv = map_resize(map, NGHTTP2_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; } @@ -223,7 +235,7 @@ void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) { return NULL; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { @@ -252,7 +264,7 @@ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { return NGHTTP2_ERR_INVALID_ARGUMENT; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { diff --git a/deps/nghttp2/lib/nghttp2_map.h b/deps/nghttp2/lib/nghttp2_map.h index 5adfb78d027679..e45685bce71768 100644 --- a/deps/nghttp2/lib/nghttp2_map.h +++ b/deps/nghttp2/lib/nghttp2_map.h @@ -28,7 +28,7 @@ #ifdef HAVE_CONFIG_H # include -#endif /* HAVE_CONFIG_H */ +#endif /* defined(HAVE_CONFIG_H) */ #include @@ -47,6 +47,7 @@ typedef struct nghttp2_map_bucket { typedef struct nghttp2_map { nghttp2_map_bucket *table; nghttp2_mem *mem; + uint32_t seed; size_t size; size_t hashbits; } nghttp2_map; @@ -54,7 +55,7 @@ typedef struct nghttp2_map { /* * nghttp2_map_init initializes the map |map|. */ -void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); +void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem); /* * nghttp2_map_free deallocates any resources allocated for |map|. @@ -123,6 +124,6 @@ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr), #ifndef WIN32 void nghttp2_map_print_distance(const nghttp2_map *map); -#endif /* !WIN32 */ +#endif /* !defined(WIN32) */ -#endif /* NGHTTP2_MAP_H */ +#endif /* !defined(NGHTTP2_MAP_H) */ diff --git a/deps/nghttp2/lib/nghttp2_option.c b/deps/nghttp2/lib/nghttp2_option.c index 02a24eee6b2605..7e44a241ba1eec 100644 --- a/deps/nghttp2/lib/nghttp2_option.c +++ b/deps/nghttp2/lib/nghttp2_option.c @@ -155,3 +155,10 @@ void nghttp2_option_set_max_continuations(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_CONTINUATIONS; option->max_continuations = val; } + +void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option, + uint64_t burst, uint64_t rate) { + option->opt_set_mask |= NGHTTP2_OPT_GLITCH_RATE_LIMIT; + option->glitch_burst = burst; + option->glitch_rate = rate; +} diff --git a/deps/nghttp2/lib/nghttp2_option.h b/deps/nghttp2/lib/nghttp2_option.h index c89cb97f8bb685..78fa21e4a77ac8 100644 --- a/deps/nghttp2/lib/nghttp2_option.h +++ b/deps/nghttp2/lib/nghttp2_option.h @@ -72,6 +72,7 @@ typedef enum { NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14, NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT = 1 << 15, NGHTTP2_OPT_MAX_CONTINUATIONS = 1 << 16, + NGHTTP2_OPT_GLITCH_RATE_LIMIT = 1 << 17, } nghttp2_option_flag; /** @@ -83,6 +84,11 @@ struct nghttp2_option { */ uint64_t stream_reset_burst; uint64_t stream_reset_rate; + /** + * NGHTTP2_OPT_GLITCH_RATE_LIMIT + */ + uint64_t glitch_burst; + uint64_t glitch_rate; /** * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH */ diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c index dc1a00cf576917..a53c7420b36952 100644 --- a/deps/nghttp2/lib/nghttp2_session.c +++ b/deps/nghttp2/lib/nghttp2_session.c @@ -438,6 +438,7 @@ static int session_new(nghttp2_session **session_ptr, size_t max_deflate_dynamic_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; size_t i; + uint32_t map_seed; if (mem == NULL) { mem = nghttp2_mem_default(); @@ -474,6 +475,10 @@ static int session_new(nghttp2_session **session_ptr, NGHTTP2_DEFAULT_STREAM_RESET_BURST, NGHTTP2_DEFAULT_STREAM_RESET_RATE); + nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim, + NGHTTP2_DEFAULT_GLITCH_BURST, + NGHTTP2_DEFAULT_GLITCH_RATE); + if (server) { (*session_ptr)->server = 1; } @@ -566,6 +571,11 @@ static int session_new(nghttp2_session **session_ptr, if (option->opt_set_mask & NGHTTP2_OPT_MAX_CONTINUATIONS) { (*session_ptr)->max_continuations = option->max_continuations; } + + if (option->opt_set_mask & NGHTTP2_OPT_GLITCH_RATE_LIMIT) { + nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim, + option->glitch_burst, option->glitch_rate); + } } rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, @@ -594,7 +604,13 @@ static int session_new(nghttp2_session **session_ptr, goto fail_aob_framebuf; } - nghttp2_map_init(&(*session_ptr)->streams, mem); + if (callbacks->rand_callback) { + callbacks->rand_callback((uint8_t *)&map_seed, sizeof(map_seed)); + } else { + map_seed = 0; + } + + nghttp2_map_init(&(*session_ptr)->streams, map_seed, mem); active_outbound_item_reset(&(*session_ptr)->aob, mem); @@ -3367,11 +3383,26 @@ static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session, return 0; } +static int session_update_glitch_ratelim(nghttp2_session *session) { + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + + nghttp2_ratelim_update(&session->glitch_ratelim, nghttp2_time_now_sec()); + + if (nghttp2_ratelim_drain(&session->glitch_ratelim, 1) == 0) { + return 0; + } + + return nghttp2_session_terminate_session(session, NGHTTP2_ENHANCE_YOUR_CALM); +} + static int session_handle_invalid_stream2(nghttp2_session *session, int32_t stream_id, nghttp2_frame *frame, int lib_error_code) { int rv; + rv = nghttp2_session_add_rst_stream( session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); if (rv != 0) { @@ -5444,6 +5475,16 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { DEBUGF("recv: DATA not allowed stream_id=%d\n", iframe->frame.hd.stream_id); + + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + iframe->state = NGHTTP2_IB_IGN_DATA; break; } @@ -5469,6 +5510,20 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, break; } + /* Empty DATA frame without END_STREAM flag set is + suspicious. */ + if (iframe->payloadleft == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + } + iframe->state = NGHTTP2_IB_READ_DATA; break; } @@ -5545,6 +5600,15 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, } if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } @@ -5565,6 +5629,18 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, break; } + /* This is deprecated RFC 7540 priorities mechanism which is + very unpopular. We do not expect it is received so + frequently. */ + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + iframe->state = NGHTTP2_IB_READ_NBYTE; inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); @@ -5737,8 +5813,17 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, if (check_ext_type_set(session->user_recv_ext_types, iframe->frame.hd.type)) { if (!session->callbacks.unpack_extension_callback) { - /* Silently ignore unknown frame type. */ + /* Receiving too frequent unknown frames is suspicious. */ + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + /* Silently ignore unknown frame type. */ busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; @@ -5850,6 +5935,17 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, break; } + /* Receiving too frequent PRIORITY_UPDATE is + suspicious. */ + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + if (iframe->payloadleft > sizeof(iframe->raw_sbuf)) { busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; @@ -5863,6 +5959,16 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, break; default: + /* Receiving too frequent unknown frames is suspicious. */ + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + busy = 1; iframe->state = NGHTTP2_IB_IGN_PAYLOAD; @@ -5959,6 +6065,15 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, } if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; break; } @@ -6497,6 +6612,20 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, iframe->frame.data.padlen = (size_t)padlen; + /* Empty DATA frame without END_STREAM flag set is + suspicious. */ + if (iframe->payloadleft == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + rv = session_update_glitch_ratelim(session); + if (rv != 0) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (nghttp2_ssize)inlen; + } + } + iframe->state = NGHTTP2_IB_READ_DATA; break; diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h index 419d23b5a9617e..25f300d23f131c 100644 --- a/deps/nghttp2/lib/nghttp2_session.h +++ b/deps/nghttp2/lib/nghttp2_session.h @@ -106,6 +106,10 @@ typedef struct { #define NGHTTP2_DEFAULT_STREAM_RESET_BURST 1000 #define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33 +/* The default values for glitch rate limiter. */ +#define NGHTTP2_DEFAULT_GLITCH_BURST 1000 +#define NGHTTP2_DEFAULT_GLITCH_RATE 33 + /* The default max number of CONTINUATION frames following an incoming HEADER frame. */ #define NGHTTP2_DEFAULT_MAX_CONTINUATIONS 8 @@ -229,6 +233,8 @@ struct nghttp2_session { /* Stream reset rate limiter. If receiving excessive amount of stream resets, GOAWAY will be sent. */ nghttp2_ratelim stream_reset_ratelim; + /* Rate limiter for all kinds of glitches. */ + nghttp2_ratelim glitch_ratelim; /* Sequential number across all streams to process streams in FIFO. */ uint64_t stream_seq; diff --git a/deps/nghttp2/lib/nghttp2_submit.c b/deps/nghttp2/lib/nghttp2_submit.c index 5c6390fa759fd8..8a90f714e4a4b5 100644 --- a/deps/nghttp2/lib/nghttp2_submit.c +++ b/deps/nghttp2/lib/nghttp2_submit.c @@ -487,7 +487,7 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, return 0; fail_item_malloc: - free(buf); + nghttp2_mem_free(mem, buf); return rv; } @@ -570,7 +570,7 @@ int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, return 0; fail_item_malloc: - free(ov_copy); + nghttp2_mem_free(mem, ov_copy); return rv; } @@ -642,7 +642,7 @@ int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags, return 0; fail_item_malloc: - free(buf); + nghttp2_mem_free(mem, buf); return rv; }