diff options
234 files changed, 16 insertions, 83440 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 47f7380d36f..a0e26501434 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -39,7 +39,7 @@ /doc/api/net.md @nodejs/net /lib/dgram.js @nodejs/net /lib/dns.js @nodejs/net -/lib/net.js @nodejs/net @nodejs/quic +/lib/net.js @nodejs/net /lib/internal/dgram.js @nodejs/net /lib/internal/dns/* @nodejs/net /lib/internal/net.js @nodejs/net @@ -59,7 +59,7 @@ /lib/crypto.js @nodejs/crypto /lib/tls.js @nodejs/crypto @nodejs/net /src/node_crypto* @nodejs/crypto -/src/crypto/* @nodejs/crypto @nodejs/quic +/src/crypto/* @nodejs/crypto # http @@ -68,7 +68,7 @@ /lib/_http_* @nodejs/http @nodejs/net /lib/http.js @nodejs/http @nodejs/net /lib/https.js @nodejs/crypto @nodejs/net @nodejs/http -/src/node_http_common* @nodejs/http @nodejs/http2 @nodejs/quic @nodejs/net +/src/node_http_common* @nodejs/http @nodejs/http2 @nodejs/net /src/node_http_parser.cc @nodejs/http @nodejs/net # http2 @@ -80,15 +80,6 @@ /src/node_http2* @nodejs/http2 @nodejs/net /src/node_mem* @nodejs/http2 -# quic - -/deps/ngtcp2/ @nodejs/quic -/deps/nghttp3/ @nodejs/quic -/doc/api/quic.md @nodejs/quic -/lib/internal/quic/ @nodejs/quic -/src/node_bob* @nodejs/quic -/src/quic/ @nodejs/quic - # modules /doc/api/modules.md @nodejs/modules diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 2a021723e6f..d3e1e433924 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -14,7 +14,7 @@ env: FLAKY_TESTS: dontcare jobs: - build-windows-with-quic: + build-windows: if: github.event.pull_request.draft == false runs-on: windows-latest steps: @@ -28,4 +28,4 @@ jobs: - name: Environment Information run: npx envinfo - name: Build - run: ./vcbuild.bat experimental-quic + run: ./vcbuild.bat diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 9d9b007b8f6..41968139816 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -29,19 +29,3 @@ jobs: run: make build-ci -j2 V=1 CONFIG_FLAGS="--error-on-warn" - name: Test run: make run-ci -j2 V=1 TEST_CI_ARGS="-p actions" - - test-linux-with-quic: - if: github.event.pull_request.draft == false - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 - with: - python-version: ${{ env.PYTHON_VERSION }} - - name: Environment Information - run: npx envinfo - - name: Build - run: make build-ci -j2 V=1 CONFIG_FLAGS="--error-on-warn --experimental-quic" - - name: Test - run: make run-ci -j2 V=1 TEST_CI_ARGS="-p actions" diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index a476e6a08c5..462e2ea35b4 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -14,7 +14,7 @@ env: FLAKY_TESTS: dontcare jobs: - test-macOS-with-quic: + test-macOS: if: github.event.pull_request.draft == false runs-on: macos-latest steps: @@ -26,6 +26,6 @@ jobs: - name: Environment Information run: npx envinfo - name: Build - run: make build-ci -j2 V=1 CONFIG_FLAGS="--error-on-warn --experimental-quic" + run: make build-ci -j2 V=1 CONFIG_FLAGS="--error-on-warn" - name: Test run: make run-ci -j2 V=1 TEST_CI_ARGS="-p actions" @@ -1333,58 +1333,6 @@ The externally maintained libraries used by Node.js are: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -- ngtcp2, located at deps/ngtcp2, is licensed as follows: - """ - The MIT License - - Copyright (c) 2016 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. - """ - -- nghttp3, located at deps/nghttp3, is licensed as follows: - """ - The MIT License - - Copyright (c) 2019 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. - """ - - node-inspect, located at deps/node-inspect, is licensed as follows: """ Copyright Node.js contributors. All rights reserved. diff --git a/configure.py b/configure.py index 8ed8524b266..096d40a13ab 100755 --- a/configure.py +++ b/configure.py @@ -122,12 +122,6 @@ parser.add_argument('--error-on-warn', default=None, help='Turn compiler warnings into errors for node core sources.') -parser.add_argument('--experimental-quic', - action='store_true', - dest='experimental_quic', - default=None, - help='enable experimental quic support') - parser.add_argument('--gdb', action='store_true', dest='gdb', @@ -289,50 +283,6 @@ shared_optgroup.add_argument('--shared-nghttp2-libpath', dest='shared_nghttp2_libpath', help='a directory to search for the shared nghttp2 DLLs') -shared_optgroup.add_argument('--shared-ngtcp2', - action='store_true', - dest='shared_ngtcp2', - default=None, - help='link to a shared ngtcp2 DLL instead of static linking') - -shared_optgroup.add_argument('--shared-ngtcp2-includes', - action='store', - dest='shared_ngtcp2_includes', - help='directory containing ngtcp2 header files') - -shared_optgroup.add_argument('--shared-ngtcp2-libname', - action='store', - dest='shared_ngtcp2_libname', - default='ngtcp2', - help='alternative lib name to link to [default: %(default)s]') - -shared_optgroup.add_argument('--shared-ngtcp2-libpath', - action='store', - dest='shared_ngtcp2_libpath', - help='a directory to search for the shared ngtcp2 DLLs') - -shared_optgroup.add_argument('--shared-nghttp3', - action='store_true', - dest='shared_nghttp3', - default=None, - help='link to a shared nghttp3 DLL instead of static linking') - -shared_optgroup.add_argument('--shared-nghttp3-includes', - action='store', - dest='shared_nghttp3_includes', - help='directory containing nghttp3 header files') - -shared_optgroup.add_argument('--shared-nghttp3-libname', - action='store', - dest='shared_nghttp3_libname', - default='nghttp3', - help='alternative lib name to link to [default: %(default)s]') - -shared_optgroup.add_argument('--shared-nghttp3-libpath', - action='store', - dest='shared_nghttp3_libpath', - help='a directory to search for the shared nghttp3 DLLs') - shared_optgroup.add_argument('--shared-openssl', action='store_true', dest='shared_openssl', @@ -1290,14 +1240,6 @@ def configure_node(o): else: o['variables']['debug_nghttp2'] = 'false' - if options.experimental_quic: - if options.shared_openssl: - raise Exception('QUIC requires a modified version of OpenSSL and ' - 'cannot be enabled when using --shared-openssl.') - o['variables']['experimental_quic'] = 1 - else: - o['variables']['experimental_quic'] = 'false' - o['variables']['node_no_browser_globals'] = b(options.no_browser_globals) o['variables']['node_shared'] = b(options.shared) @@ -1421,8 +1363,6 @@ def configure_openssl(o): without_ssl_error('--openssl-fips') if options.openssl_default_cipher_list: without_ssl_error('--openssl-default-cipher-list') - if options.experimental_quic: - without_ssl_error('--experimental-quic') return if options.use_openssl_ca_store: diff --git a/deps/nghttp3/COPYING b/deps/nghttp3/COPYING deleted file mode 100644 index 37562ea58cd..00000000000 --- a/deps/nghttp3/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2019 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. diff --git a/deps/nghttp3/lib/includes/config.h b/deps/nghttp3/lib/includes/config.h deleted file mode 100644 index 0aee7749bae..00000000000 --- a/deps/nghttp3/lib/includes/config.h +++ /dev/null @@ -1,39 +0,0 @@ - -/* Edited to match src/node.h. */ -#include <stdint.h> - -#ifdef _WIN32 -#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) -typedef intptr_t ssize_t; -# define _SSIZE_T_ -# define _SSIZE_T_DEFINED -#endif -#else // !_WIN32 -# include <sys/types.h> // size_t, ssize_t -#endif // _WIN32 - -#ifdef _MSC_VER -# include <intrin.h> -# define __builtin_popcount __popcnt -#endif - -/* Define to 1 to enable debug output. */ -/* #undef DEBUGBUILD */ - -/* Define to 1 if you have the <arpa/inet.h> header file. */ -/* #undef HAVE_ARPA_INET_H */ - -/* Define to 1 if you have the <stddef.h> header file. */ -#define HAVE_STDDEF_H 1 - -/* Define to 1 if you have the <stdint.h> header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the <string.h> header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the <unistd.h> header file. */ -/* #undef HAVE_UNISTD_H */ diff --git a/deps/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/nghttp3/lib/includes/nghttp3/nghttp3.h deleted file mode 100644 index 3ace46856b7..00000000000 --- a/deps/nghttp3/lib/includes/nghttp3/nghttp3.h +++ /dev/null @@ -1,1801 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2018 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2017 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. - */ -#ifndef NGHTTP3_H -#define NGHTTP3_H - -/* Define WIN32 when build target is Win32 API (borrowed from - libcurl) */ -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -# define WIN32 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdlib.h> -#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 <stdint.h> -#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ -# include <inttypes.h> -#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ -#include <sys/types.h> -#include <stdarg.h> -#include <stddef.h> - -#include <nghttp3/version.h> - -#ifdef NGHTTP3_STATICLIB -# define NGHTTP3_EXTERN -#elif defined(WIN32) -# ifdef BUILDING_NGHTTP3 -# define NGHTTP3_EXTERN __declspec(dllexport) -# else /* !BUILDING_NGHTTP3 */ -# define NGHTTP3_EXTERN __declspec(dllimport) -# endif /* !BUILDING_NGHTTP3 */ -#else /* !defined(WIN32) */ -# ifdef BUILDING_NGHTTP3 -# define NGHTTP3_EXTERN __attribute__((visibility("default"))) -# else /* !BUILDING_NGHTTP3 */ -# define NGHTTP3_EXTERN -# endif /* !BUILDING_NGHTTP3 */ -#endif /* !defined(WIN32) */ - -typedef ptrdiff_t nghttp3_ssize; - -/* NGHTTP3_ALPN_H3 is a serialized form of HTTP/3 ALPN protocol - identifier this library supports. Notice that the first byte is - the length of the following protocol identifier. */ -#define NGHTTP3_ALPN_H3 "\x5h3-29" - -typedef enum { - NGHTTP3_ERR_INVALID_ARGUMENT = -101, - NGHTTP3_ERR_NOBUF = -102, - NGHTTP3_ERR_INVALID_STATE = -103, - NGHTTP3_ERR_WOULDBLOCK = -104, - NGHTTP3_ERR_STREAM_IN_USE = -105, - NGHTTP3_ERR_PUSH_ID_BLOCKED = -106, - NGHTTP3_ERR_MALFORMED_HTTP_HEADER = -107, - NGHTTP3_ERR_REMOVE_HTTP_HEADER = -108, - NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING = -109, - NGHTTP3_ERR_TOO_LATE = -110, - NGHTTP3_ERR_QPACK_FATAL = -111, - NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE = -112, - NGHTTP3_ERR_IGNORE_STREAM = -113, - NGHTTP3_ERR_STREAM_NOT_FOUND = -114, - NGHTTP3_ERR_IGNORE_PUSH_PROMISE = -115, - NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED = -402, - NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR = -403, - NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR = -404, - NGHTTP3_ERR_H3_FRAME_UNEXPECTED = -408, - NGHTTP3_ERR_H3_FRAME_ERROR = -409, - NGHTTP3_ERR_H3_MISSING_SETTINGS = -665, - NGHTTP3_ERR_H3_INTERNAL_ERROR = -667, - NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM = -668, - NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR = -669, - NGHTTP3_ERR_H3_ID_ERROR = -670, - NGHTTP3_ERR_H3_SETTINGS_ERROR = -671, - NGHTTP3_ERR_H3_STREAM_CREATION_ERROR = -672, - NGHTTP3_ERR_FATAL = -900, - NGHTTP3_ERR_NOMEM = -901, - NGHTTP3_ERR_CALLBACK_FAILURE = -902 -} nghttp3_lib_error; - -#define NGHTTP3_H3_NO_ERROR 0x0100 -#define NGHTTP3_H3_GENERAL_PROTOCOL_ERROR 0x0101 -#define NGHTTP3_H3_INTERNAL_ERROR 0x0102 -#define NGHTTP3_H3_STREAM_CREATION_ERROR 0x0103 -#define NGHTTP3_H3_CLOSED_CRITICAL_STREAM 0x0104 -#define NGHTTP3_H3_FRAME_UNEXPECTED 0x0105 -#define NGHTTP3_H3_FRAME_ERROR 0x0106 -#define NGHTTP3_H3_EXCESSIVE_LOAD 0x0107 -#define NGHTTP3_H3_ID_ERROR 0x0108 -#define NGHTTP3_H3_SETTINGS_ERROR 0x0109 -#define NGHTTP3_H3_MISSING_SETTINGS 0x010a -#define NGHTTP3_H3_REQUEST_REJECTED 0x010b -#define NGHTTP3_H3_REQUEST_CANCELLED 0x010c -#define NGHTTP3_H3_REQUEST_INCOMPLETE 0x010d -#define NGHTTP3_H3_CONNECT_ERROR 0x010f -#define NGHTTP3_H3_VERSION_FALLBACK 0x0110 -#define NGHTTP3_QPACK_DECOMPRESSION_FAILED 0x0200 -#define NGHTTP3_QPACK_ENCODER_STREAM_ERROR 0x0201 -#define NGHTTP3_QPACK_DECODER_STREAM_ERROR 0x0202 - -/** - * @functypedef - * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. - */ -typedef void *(*nghttp3_malloc)(size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`nghttp3_mem` structure. - */ -typedef void (*nghttp3_free)(void *ptr, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. - */ -typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. - */ -typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data); - -/** - * @struct - * - * Custom memory allocator functions and user defined pointer. The - * |mem_user_data| member is passed to each allocator function. This - * can be used, for example, to achieve per-session memory pool. - * - * In the following example code, ``my_malloc``, ``my_free``, - * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: - * - * void *my_malloc_cb(size_t size, void *mem_user_data) { - * return my_malloc(size); - * } - * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } - * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { - * return my_calloc(nmemb, size); - * } - * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { - * return my_realloc(ptr, size); - * } - * - * void conn_new() { - * nghttp3_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, - * my_realloc_cb}; - * - * ... - * } - */ -typedef struct { - /** - * An arbitrary user supplied data. This is passed to each - * allocator function. - */ - void *mem_user_data; - /** - * Custom allocator function to replace malloc(). - */ - nghttp3_malloc malloc; - /** - * Custom allocator function to replace free(). - */ - nghttp3_free free; - /** - * Custom allocator function to replace calloc(). - */ - nghttp3_calloc calloc; - /** - * Custom allocator function to replace realloc(). - */ - nghttp3_realloc realloc; -} nghttp3_mem; - -/** - * @function - * - * `nghttp3_mem_default` returns the default memory allocator which - * uses malloc/calloc/realloc/free. - */ -NGHTTP3_EXTERN const nghttp3_mem *nghttp3_mem_default(void); - -/** - * @struct - * - * nghttp3_vec is struct iovec compatible structure to reference - * arbitrary array of bytes. - */ -typedef struct { - /** - * base points to the data. - */ - uint8_t *base; - /** - * len is the number of bytes which the buffer pointed by base - * contains. - */ - size_t len; -} nghttp3_vec; - -struct nghttp3_rcbuf; - -/** - * @struct - * - * :type:`nghttp3_rcbuf` is the object representing reference counted - * buffer. The details of this structure are intentionally hidden - * from the public API. - */ -typedef struct nghttp3_rcbuf nghttp3_rcbuf; - -/** - * @function - * - * `nghttp3_rcbuf_incref` increments the reference count of |rcbuf| by - * 1. - */ -NGHTTP3_EXTERN void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf); - -/** - * @function - * - * `nghttp3_rcbuf_decref` decrements the reference count of |rcbuf| by - * 1. If the reference count becomes zero, the object pointed by - * |rcbuf| will be freed. In this case, application must not use - * |rcbuf| again. - */ -NGHTTP3_EXTERN void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf); - -/** - * @function - * - * `nghttp3_rcbuf_get_buf` returns the underlying buffer managed by - * |rcbuf|. - */ -NGHTTP3_EXTERN nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf); - -/** - * @function - * - * `nghttp3_rcbuf_is_static` returns nonzero if the underlying buffer - * is statically allocated, and 0 otherwise. This can be useful for - * language bindings that wish to avoid creating duplicate strings for - * these buffers. - */ -NGHTTP3_EXTERN int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf); - -/** - * @struct - * - * :type:`nghttp3_buf` is the variable size buffer. - */ -typedef struct { - /** - * begin points to the beginning of the buffer. - */ - uint8_t *begin; - /** - * end points to the one beyond of the last byte of the buffer - */ - uint8_t *end; - /** - * pos pointers to the start of data. Typically, this points to the - * point that next data should be read. Initially, it points to - * |begin|. - */ - uint8_t *pos; - /** - * last points to the one beyond of the last data of the buffer. - * Typically, new data is written at this point. Initially, it - * points to |begin|. - */ - uint8_t *last; -} nghttp3_buf; - -/** - * @function - * - * `nghttp3_buf_init` initializes empty |buf|. - */ -NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf); - -/** - * @function - * - * `nghttp3_buf_free` frees resources allocated for |buf| using |mem| - * as memory allocator. buf->begin must be a heap buffer allocated by - * |mem|. - */ -NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem); - -/** - * @function - * - * `nghttp3_buf_left` returns the number of additional bytes which can - * be written to the underlying buffer. In other words, it returns - * buf->end - buf->last. - */ -NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf); - -/** - * @function - * - * `nghttp3_buf_len` returns the number of bytes left to read. In - * other words, it returns buf->last - buf->pos. - */ -NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf); - -/** - * @function - * - * `nghttp3_buf_reset` sets buf->pos and buf->last to buf->begin. - */ -NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); - -/** - * @enum - * - * :type:`nghttp3_nv_flag` is the flags for header field name/value - * pair. - */ -typedef enum { - /** - * :enum:`NGHTTP3_NV_FLAG_NONE` indicates no flag set. - */ - NGHTTP3_NV_FLAG_NONE = 0, - /** - * :enum:`NGHTTP3_NV_FLAG_NEVER_INDEX` indicates that this - * name/value pair must not be indexed. Other implementation calls - * this bit as "sensitive". - */ - NGHTTP3_NV_FLAG_NEVER_INDEX = 0x01, - /** - * :enum:`NGHTTP3_NV_FLAG_NO_COPY_NAME` is set solely by - * application. If this flag is set, the library does not make a - * copy of header field name. This could improve performance. - */ - NGHTTP3_NV_FLAG_NO_COPY_NAME = 0x02, - /** - * :enum:`NGHTTP3_NV_FLAG_NO_COPY_VALUE` is set solely by - * application. If this flag is set, the library does not make a - * copy of header field value. This could improve performance. - */ - NGHTTP3_NV_FLAG_NO_COPY_VALUE = 0x04 -} nghttp3_nv_flag; - -/** - * @struct - * - * :type:`nghttp3_nv` is the name/value pair, which mainly used to - * represent header fields. - */ -typedef struct { - /** - * name is the header field name. - */ - uint8_t *name; - /** - * value is the header field value. - */ - uint8_t *value; - /** - * namelen is the length of the |name|, excluding terminating NULL. - */ - size_t namelen; - /** - * valuelen is the length of the |value|, excluding terminating - * NULL. - */ - size_t valuelen; - /** - * flags is bitwise OR of one or more of :type:`nghttp3_nv_flag`. - */ - uint8_t flags; -} nghttp3_nv; - -/* Generated by mkstatichdtbl.py */ -typedef enum { - NGHTTP3_QPACK_TOKEN__AUTHORITY = 0, - NGHTTP3_QPACK_TOKEN__PATH = 8, - NGHTTP3_QPACK_TOKEN_AGE = 43, - NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION = 52, - NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH = 55, - NGHTTP3_QPACK_TOKEN_COOKIE = 68, - NGHTTP3_QPACK_TOKEN_DATE = 69, - NGHTTP3_QPACK_TOKEN_ETAG = 71, - NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE = 74, - NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH = 75, - NGHTTP3_QPACK_TOKEN_LAST_MODIFIED = 77, - NGHTTP3_QPACK_TOKEN_LINK = 78, - NGHTTP3_QPACK_TOKEN_LOCATION = 79, - NGHTTP3_QPACK_TOKEN_REFERER = 83, - NGHTTP3_QPACK_TOKEN_SET_COOKIE = 85, - NGHTTP3_QPACK_TOKEN__METHOD = 1, - NGHTTP3_QPACK_TOKEN__SCHEME = 9, - NGHTTP3_QPACK_TOKEN__STATUS = 11, - NGHTTP3_QPACK_TOKEN_ACCEPT = 25, - NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING = 27, - NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES = 29, - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS = 32, - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 38, - NGHTTP3_QPACK_TOKEN_CACHE_CONTROL = 46, - NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING = 53, - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE = 57, - NGHTTP3_QPACK_TOKEN_RANGE = 82, - NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY = 86, - NGHTTP3_QPACK_TOKEN_VARY = 92, - NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS = 94, - NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION = 98, - NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE = 28, - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS = 30, - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS = 35, - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS = 39, - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS = 40, - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD = 41, - NGHTTP3_QPACK_TOKEN_ALT_SVC = 44, - NGHTTP3_QPACK_TOKEN_AUTHORIZATION = 45, - NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY = 56, - NGHTTP3_QPACK_TOKEN_EARLY_DATA = 70, - NGHTTP3_QPACK_TOKEN_EXPECT_CT = 72, - NGHTTP3_QPACK_TOKEN_FORWARDED = 73, - NGHTTP3_QPACK_TOKEN_IF_RANGE = 76, - NGHTTP3_QPACK_TOKEN_ORIGIN = 80, - NGHTTP3_QPACK_TOKEN_PURPOSE = 81, - NGHTTP3_QPACK_TOKEN_SERVER = 84, - NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN = 89, - NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS = 90, - NGHTTP3_QPACK_TOKEN_USER_AGENT = 91, - NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR = 95, - NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS = 96, - /* Additional header fields for HTTP messaging validation */ - NGHTTP3_QPACK_TOKEN_HOST = 1000, - NGHTTP3_QPACK_TOKEN_CONNECTION, - NGHTTP3_QPACK_TOKEN_KEEP_ALIVE, - NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION, - NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING, - NGHTTP3_QPACK_TOKEN_UPGRADE, - NGHTTP3_QPACK_TOKEN_TE, - NGHTTP3_QPACK_TOKEN__PROTOCOL, - NGHTTP3_QPACK_TOKEN_PRIORITY -} nghttp3_qpack_token; - -/** - * @struct - * - * nghttp3_qpack_nv represents header field name/value pair just like - * :type:`nghttp3_nv`. It is an extended version of - * :type:`nghttp3_nv` and has reference counted buffers and tokens - * which might be useful for applications. - */ -typedef struct { - /* The buffer containing header field name. NULL-termination is - guaranteed. */ - nghttp3_rcbuf *name; - /* The buffer containing header field value. NULL-termination is - guaranteed. */ - nghttp3_rcbuf *value; - /* nghttp3_qpack_token value for name. It could be -1 if we have no - token for that header field name. */ - int32_t token; - /* Bitwise OR of one or more of nghttp3_nv_flag. */ - uint8_t flags; -} nghttp3_qpack_nv; - -struct nghttp3_qpack_encoder; - -/** - * @struct - * - * :type:`nghttp3_qpack_encoder` is QPACK encoder. - */ -typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; - -/** - * @function - * - * `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder| - * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic - * table size. |max_blocked| is the maximum number of streams which - * can be blocked. |mem| is a memory allocator. This function - * allocates memory for :type:`nghttp3_qpack_encoder` itself and - * assigns its pointer to |*pencoder| if it succeeds. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - */ -NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t max_dtable_size, - size_t max_blocked, - const nghttp3_mem *mem); - -/** - * @function - * - * `nghttp3_qpack_encoder_del` frees memory allocated for |encoder|. - * This function frees memory pointed by |encoder| itself. - */ -NGHTTP3_EXTERN void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder); - -/** - * @function - * - * `nghttp3_qpack_encoder_encode` encodes the list of header fields - * |nva|. |nvlen| is the length of |nva|. |stream_id| is the - * identifier of the stream which this header fields belong to. This - * function writes header block prefix, encoded header fields, and - * encoder stream to |pbuf|, |rbuf|, and |ebuf| respectively. The - * last field of nghttp3_buf will be adjusted when data is written. - * An application should write |pbuf| and |rbuf| to the request stream - * in this order. - * - * The buffer pointed by |pbuf|, |rbuf|, and |ebuf| can be empty - * buffer. It is fine to pass a buffer initialized by - * nghttp3_buf_init(buf). This function allocates memory for these - * buffers as necessary. In particular, it frees and expands buffer - * if the current capacity of buffer is not enough. If begin field of - * any buffer is not NULL, it must be allocated by the same memory - * allocator passed to `nghttp3_qpack_encoder_new()`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory - * :enum:`NGHTTP3_ERR_QPACK_FATAL` - * |encoder| is in unrecoverable error state and cannot be used - * anymore. - */ -NGHTTP3_EXTERN int nghttp3_qpack_encoder_encode( - nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, nghttp3_buf *rbuf, - nghttp3_buf *ebuf, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen); - -/** - * @function - * - * `nghttp3_qpack_encoder_read_decoder` reads decoder stream. The - * buffer pointed by |src| of length |srclen| contains decoder stream. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory - * :enum:`NGHTTP3_ERR_QPACK_FATAL` - * |encoder| is in unrecoverable error state and cannot be used - * anymore. - * :enum:`NGHTTP3_ERR_QPACK_DECODER_STREAM` - * |encoder| is unable to process input because it is malformed. - */ -NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder( - nghttp3_qpack_encoder *encoder, const uint8_t *src, size_t srclen); - -/** - * @function - * - * `nghttp3_qpack_encoder_set_max_dtable_size` sets max dynamic table - * size to |max_dtable_size|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * :enum:`NGHTTP3_ERR_INVALID_ARGUMENT` - * |max_dtable_size| exceeds the hard limit that decoder specifies. - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size); - -/** - * @function - * - * `nghttp3_qpack_encoder_set_hard_max_dtable_size` sets hard maximum - * dynamic table size to |hard_max_dtable_size|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * TBD - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_hard_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t hard_max_dtable_size); - -/** - * @function - * - * `nghttp3_qpack_encoder_set_max_blocked` sets the number of streams - * which can be blocked to |max_blocked|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * TBD - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, - size_t max_blocked); - -/** - * @function - * - * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header - * block for a stream denoted by |stream_id| was acknowledged by - * decoder. This function is provided for debugging purpose only. In - * HTTP/3, |encoder| knows acknowledgement of header block by reading - * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_qpack_encoder_add_insert_count` increments known received - * count of |encoder| by |n|. This function is provided for debugging - * purpose only. In HTTP/3, |encoder| increments known received count - * by reading decoder stream with - * `nghttp3_qpack_encoder_read_decoder()`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP3_QPACK_DECODER_STREAM` - * |n| is too large. - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, - uint64_t n); - -/** - * @function - * - * `nghttp3_qpack_encoder_ack_everything` tells |encoder| that all - * encoded header blocks are acknowledged. This function is provided - * for debugging purpose only. In HTTP/3, |encoder| knows this by - * reading decoder stream with `nghttp3_qpack_encoder_read_decoder()`. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder); - -/** - * @function - * - * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream - * denoted by |stream_id| is cancelled. This function is provided for - * debugging purpose only. In HTTP/3, |encoder| knows this by reading - * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_qpack_encoder_get_num_blocked` returns the number of - * streams which is potentially blocked at decoder side. - */ -NGHTTP3_EXTERN size_t -nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder); - -struct nghttp3_qpack_stream_context; - -/** - * @struct - * - * :type:`nghttp3_qpack_stream_context` is a decoder context for an - * individual stream. - */ -typedef struct nghttp3_qpack_stream_context nghttp3_qpack_stream_context; - -/** - * @function - * - * `nghttp3_qpack_stream_context_new` initializes stream context. - * |psctx| must be non-NULL pointer. |stream_id| is stream ID. |mem| - * is a memory allocator. This function allocates memory for - * :type:`nghttp3_qpack_stream_context` itself and assigns its pointer - * to |*psctx| if it succeeds. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - */ -NGHTTP3_EXTERN int -nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, - int64_t stream_id, const nghttp3_mem *mem); - -/** - * @function - * - * `nghttp3_qpack_stream_context_del` frees memory allocated for - * |sctx|. This function frees memory pointed by |sctx| itself. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx); - -/** - * @function - * - * `nghttp3_qpack_stream_context_get_ricnt` returns required insert - * count. - */ -NGHTTP3_EXTERN uint64_t -nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx); - -struct nghttp3_qpack_decoder; - -/** - * @struct - * - * `nghttp3_qpack_decoder` is QPACK decoder. - */ -typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; - -/** - * @function - * - * `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder| - * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic - * table size. |max_blocked| is the maximum number of streams which - * can be blocked. |mem| is a memory allocator. This function - * allocates memory for :type:`nghttp3_qpack_decoder` itself and - * assigns its pointer to |*pdecoder| if it succeeds. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - */ -NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, - size_t max_dtable_size, - size_t max_blocked, - const nghttp3_mem *mem); - -/** - * @function - * - * `nghttp3_qpack_decoder_del` frees memory allocated for |decoder|. - * This function frees memory pointed by |decoder| itself. - */ -NGHTTP3_EXTERN void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder); - -/** - * @function - * - * `nghttp3_qpack_decoder_read_encoder` reads encoder stream. The - * buffer pointed by |src| of length |srclen| contains encoder stream. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP3_ERR_QPACK_FATAL` - * |decoder| is in unrecoverable error state and cannot be used - * anymore. - * :enum:`NGHTTP3_ERR_QPACK_ENCODER_STREAM` - * Could not interpret encoder stream instruction. - */ -NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_encoder( - nghttp3_qpack_decoder *decoder, const uint8_t *src, size_t srclen); - -/** - * @function - * - * `nghttp3_qpack_decoder_get_icnt` returns insert count. - */ -NGHTTP3_EXTERN uint64_t -nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); - -/** - * @enum - * - * :type:`nghttp3_qpack_decode_flag` is a set of flags for decoder. - */ -typedef enum { - /** - * :enum:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag - * set. - */ - NGHTTP3_QPACK_DECODE_FLAG_NONE, - /** - * :enum:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header - * field is successfully decoded. - */ - NGHTTP3_QPACK_DECODE_FLAG_EMIT = 0x01, - /** - * :enum:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header - * fields have been decoded. - */ - NGHTTP3_QPACK_DECODE_FLAG_FINAL = 0x02, - /** - * :enum:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding - * has been blocked. - */ - NGHTTP3_QPACK_DECODE_FLAG_BLOCKED = 0x04 -} nghttp3_qpack_decode_flag; - -/** - * @function - * - * `nghttp3_qpack_decoder_read_request` reads request stream. The - * request stream is given as the buffer pointed by |src| of length - * |srclen|. |sctx| is the stream context and it must be initialized - * by `nghttp3_qpack_stream_context_init()`. |*pflags| must be - * non-NULL pointer. |nv| must be non-NULL pointer. - * - * If this function succeeds, it assigns flags to |*pflags|. If - * |*pflags| has :enum:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a decoded - * header field is assigned to |nv|. If |*pflags| has - * :enum:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, all header fields have - * been successfully decoded. If |*pflags| has - * :enum:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked - * due to required insert count. - * - * When a header field is decoded, an application receives it in |nv|. - * nv->name and nv->value are reference counted buffer, and their - * reference counts are already incremented for application use. - * Therefore, when application finishes processing the header field, - * it must call nghttp3_rcbuf_decref(nv->name) and - * nghttp3_rcbuf_decref(nv->value) or memory leak might occur. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP3_ERR_QPACK_FATAL` - * |decoder| is in unrecoverable error state and cannot be used - * anymore. - * :enum:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` - * Could not interpret header block instruction. - * :enum:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` - * Header field is too large. - */ -NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_request( - nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv, uint8_t *pflags, const uint8_t *src, size_t srclen, - int fin); - -/** - * @function - * - * `nghttp3_qpack_decoder_write_decoder` writes decoder stream into - * |dbuf|. - * - * The caller must ensure that nghttp3_buf_left(dbuf) >= - * nghttp3_qpack_decoder_get_decoder_streamlen(decoder). - */ -NGHTTP3_EXTERN void -nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, - nghttp3_buf *dbuf); - -/** - * @function - * - * `nghttp3_qpack_decoder_get_decoder_streamlen` returns the length of - * decoder stream. - */ -NGHTTP3_EXTERN size_t -nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder); - -/** - * @function - * - * `nghttp3_qpack_decoder_cancel_stream` cancels header decoding for - * stream denoted by |stream_id|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP3_ERR_QPACK_FATAL` - * Decoder stream overflow. - */ -NGHTTP3_EXTERN int -nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_qpack_decoder_set_dtable_cap` sets |cap| as maximum - * dynamic table size. Normally, the maximum capacity is communicated - * in encoder stream. This function is provided for debugging and - * testing purpose. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, - size_t cap); - -/** - * @function - * - * `nghttp3_qpack_decoder_set_max_concurrent_streams` tells |decoder| - * the maximum number of concurrent streams that a remote endpoint can - * open, including both bidirectional and unidirectional streams which - * potentially receive QPACK encoded HEADERS frame. This value is - * used as a hint to limit the length of decoder stream. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_decoder_set_max_concurrent_streams(nghttp3_qpack_decoder *decoder, - size_t max_concurrent_streams); - -/** - * @function - * - * `nghttp3_strerror` returns textual representation of |liberr| which - * should be one of error codes defined in :type:`nghttp3_lib_error`. - */ -NGHTTP3_EXTERN const char *nghttp3_strerror(int liberr); - -/** - * @function - * - * `nghttp3_err_infer_quic_app_error_code` returns a QUIC application - * error code which corresponds to |liberr|. - */ -NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr); - -/** - * @functypedef - * - * :type:`nghttp3_debug_vprintf_callback` is a callback function - * invoked when the library outputs debug logging. The function is - * called with arguments suitable for ``vfprintf(3)`` - * - * The debug output is only enabled if the library is built with - * ``DEBUGBUILD`` macro defined. - */ -typedef void (*nghttp3_debug_vprintf_callback)(const char *format, - va_list args); - -/** - * @function - * - * `nghttp3_set_debug_vprintf_callback` sets a debug output callback - * called by the library when built with ``DEBUGBUILD`` macro defined. - * If this option is not used, debug log is written into standard - * error output. - * - * For builds without ``DEBUGBUILD`` macro defined, this function is - * noop. - * - * Note that building with ``DEBUGBUILD`` may cause significant - * performance penalty to libnghttp3 because of extra processing. It - * should be used for debugging purpose only. - * - * .. Warning:: - * - * Building with ``DEBUGBUILD`` may cause significant performance - * penalty to libnghttp3 because of extra processing. It should be - * used for debugging purpose only. We write this two times because - * this is important. - */ -NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback( - nghttp3_debug_vprintf_callback debug_vprintf_callback); - -struct nghttp3_conn; -typedef struct nghttp3_conn nghttp3_conn; - -/** - * @functypedef - * - * :type:`nghttp3_acked_stream_data` is a callback function which is - * invoked when data sent on stream denoted by |stream_id| supplied - * from application is acknowledged by remote endpoint. The number of - * bytes acknowledged is given in |datalen|. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id, - size_t datalen, void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_conn_stream_close` is a callback function which is - * invoked when a stream identified by |stream_id| is closed. - * |app_error_code| indicates the reason of this closure. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_stream_close)(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, - void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_recv_data` is a callback function which is invoked - * when a part of request or response body on stream identified by - * |stream_id| is received. |data| points to the received data and - * its length is |datalen|. - * - * The application is responsible for increasing flow control credit - * by |datalen| bytes. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_recv_data)(nghttp3_conn *conn, int64_t stream_id, - const uint8_t *data, size_t datalen, - void *conn_user_data, void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_deferred_consume` is a callback function which is - * invoked when the library consumed |consumed| bytes for a stream - * identified by |stream_id|. This callback is used to notify the - * consumed bytes for stream blocked by QPACK decoder. The - * application is responsible for increasing flow control credit by - * |consumed| bytes. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_deferred_consume)(nghttp3_conn *conn, int64_t stream_id, - size_t consumed, void *conn_user_data, - void *stream_user_data); - -typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id, - void *conn_user_data, - void *stream_user_data); - -typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id, - int32_t token, nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *conn_user_data, - void *stream_user_data); - -typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id, - void *conn_user_data, - void *stream_user_data); - -typedef int (*nghttp3_begin_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, void *conn_user_data, - void *stream_user_data); - -typedef int (*nghttp3_recv_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, int32_t token, - nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *conn_user_data, - void *stream_user_data); - -typedef int (*nghttp3_end_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_end_stream` is a callback function which is invoked - * when the receiving side of stream is closed. For server, this - * callback function is invoked when HTTP request is received - * completely. For client, this callback function is invoked when - * HTTP response is received completely. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id, - void *conn_user_data, void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_cancel_push` is a callback function which is invoked - * when the push identified by |push_id| is cancelled by remote - * endpoint. If a stream has been bound to the push ID, |stream_id| - * contains the stream ID and |stream_user_data| points to the stream - * user data. Otherwise, |stream_id| is -1 and |stream_user_data| is - * NULL. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id, void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_send_stop_sending` is a callback function which is - * invoked when the library asks application to send STOP_SENDING to - * the stream identified by |stream_id|. |app_error_code| indicates - * the reason for this action. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_send_stop_sending)(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, - void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_push_stream` is a callback function which is invoked - * when a push stream identified by |stream_id| is opened with - * |push_id|. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_push_stream)(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id, void *conn_user_data); - -typedef struct { - nghttp3_acked_stream_data acked_stream_data; - nghttp3_stream_close stream_close; - nghttp3_recv_data recv_data; - nghttp3_deferred_consume deferred_consume; - nghttp3_begin_headers begin_headers; - nghttp3_recv_header recv_header; - nghttp3_end_headers end_headers; - nghttp3_begin_headers begin_trailers; - nghttp3_recv_header recv_trailer; - nghttp3_end_headers end_trailers; - nghttp3_begin_push_promise begin_push_promise; - nghttp3_recv_push_promise recv_push_promise; - nghttp3_end_push_promise end_push_promise; - nghttp3_cancel_push cancel_push; - nghttp3_send_stop_sending send_stop_sending; - nghttp3_push_stream push_stream; - nghttp3_end_stream end_stream; -} nghttp3_conn_callbacks; - -typedef struct { - uint64_t max_field_section_size; - uint64_t max_pushes; - size_t qpack_max_table_capacity; - size_t qpack_blocked_streams; -} nghttp3_conn_settings; - -NGHTTP3_EXTERN void -nghttp3_conn_settings_default(nghttp3_conn_settings *settings); - -NGHTTP3_EXTERN int -nghttp3_conn_client_new(nghttp3_conn **pconn, - const nghttp3_conn_callbacks *callbacks, - const nghttp3_conn_settings *settings, - const nghttp3_mem *mem, void *conn_user_data); - -NGHTTP3_EXTERN int -nghttp3_conn_server_new(nghttp3_conn **pconn, - const nghttp3_conn_callbacks *callbacks, - const nghttp3_conn_settings *settings, - const nghttp3_mem *mem, void *conn_user_data); - -NGHTTP3_EXTERN void nghttp3_conn_del(nghttp3_conn *conn); - -/** - * @function - * - * `nghttp3_conn_bind_control_stream` binds stream denoted by - * |stream_id| to outgoing unidirectional control stream. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_INVALID_STATE` - * Control stream has already corresponding stream ID. - * TBD - */ -NGHTTP3_EXTERN int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_bind_qpack_streams` binds stream denoted by - * |qenc_stream_id| to outgoing QPACK encoder stream and stream - * denoted by |qdec_stream_id| to outgoing QPACK encoder stream. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_INVALID_STATE` - * QPACK encoder/decoder stream have already corresponding stream - * IDs. - * TBD - */ -NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, - int64_t qenc_stream_id, - int64_t qdec_stream_id); - -/** - * @function - * - * nghttp3_conn_read_stream reads data |src| of length |srclen| on - * stream identified by |stream_id|. It returns the number of bytes - * consumed. The "consumed" means that application can increase flow - * 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 - * type:`nghttp3_recv_data` to handle those bytes. If |fin| is - * nonzero, this is the last data from remote endpoint in this stream. - */ -NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, - int64_t stream_id, - const uint8_t *src, - size_t srclen, int fin); - -/** - * @function - * - * `nghttp3_conn_writev_stream` stores stream data to send to |vec| of - * length |veccnt| and returns the number of nghttp3_vec object in - * which it stored data. It stores stream ID to |*pstream_id|. An - * application has to call `nghttp3_conn_add_write_offset` to inform - * |conn| of the actual number of bytes that underlying QUIC stack - * accepted. |*pfin| will be nonzero if this is the last data to - * send. If there is no stream to write data or send fin, this - * function returns 0, and -1 is assigned to |*pstream_id|. - */ -NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, - int64_t *pstream_id, - int *pfin, - nghttp3_vec *vec, - size_t veccnt); - -/** - * @function - * - * `nghttp3_conn_add_write_offset` tells |conn| the number of bytes - * |n| for stream denoted by |stream_id| QUIC stack accepted. - * - * If stream has no data to send but just sends fin (closing the write - * side of a stream), the number of bytes sent is 0. It is important - * to call this function even if |n| is 0 in this case. It is safe to - * call this function if |n| is 0. - * - * `nghttp3_conn_writev_stream` must be called before calling this - * function to get data to send, and those data must be fed into QUIC - * stack. - */ -NGHTTP3_EXTERN int nghttp3_conn_add_write_offset(nghttp3_conn *conn, - int64_t stream_id, size_t n); - -/** - * @function - * - * `nghttp3_conn_add_ack_offset` tells |conn| the number of bytes |n| - * for stream denoted by |stream_id| QUIC stack has acknowledged. - */ -NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, - int64_t stream_id, uint64_t n); - -/** - * @function - * - * `nghttp3_conn_block_stream` tells the library that stream - * identified by |stream_id| is blocked due to QUIC flow control. - */ -NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_unblock_stream` tells the library that stream - * identified by |stream_id| which was blocked by QUIC flow control is - * unblocked. - */ -NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_resume_stream` resumes stream identified by - * |stream_id| which was previously unable to provide data. - */ -NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_close_stream` closes stream identified by - * |stream_id|. |app_error_code| is the reason of the closure. - */ -NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn, - int64_t stream_id, - uint64_t app_error_code); - -/** - * @function - * - * `nghttp3_conn_reset_stream` must be called if stream identified by - * |stream_id| is reset by a remote endpoint. This is required in - * order to cancel QPACK stream. - */ -NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, - int64_t stream_id); - -typedef enum { - NGHTTP3_DATA_FLAG_NONE = 0x00, - /** - * ``NGHTTP3_DATA_FLAG_EOF`` indicates that all request or response - * body has been provided to the library. It also indicates that - * sending side of stream is closed unless - * :enum:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same - * time. - */ - NGHTTP3_DATA_FLAG_EOF = 0x01, - /** - * ``NGHTTP3_DATA_FLAG_NO_END_STREAM`` indicates that sending side - * of stream is not closed even if NGHTTP3_DATA_FLAG_EOF is set. - * Usually this flag is used to send trailer fields with - * `nghttp3_conn_submit_trailers()`. If - * `nghttp3_conn_submit_trailers()` has been called, regardless of - * this flag, the submitted trailer fields are sent. - */ - NGHTTP3_DATA_FLAG_NO_END_STREAM = 0x02 -} nghttp3_data_flag; - -/** - * @function - * - * `nghttp3_conn_set_max_client_streams_bidi` tells |conn| the - * cumulative number of bidirectional streams that client can open. - */ -NGHTTP3_EXTERN void -nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn, - uint64_t max_streams); - -/** - * @function - * - * `nghttp3_conn_set_max_concurrent_streams` tells |conn| the maximum - * number of concurrent streams that a remote endpoint can open, - * including both bidirectional and unidirectional streams which - * potentially receive QPACK encoded HEADERS frame. This value is - * used as a hint to limit the internal resource consumption. - */ -NGHTTP3_EXTERN void -nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, - size_t max_concurrent_streams); - -/** - * @functypedef - * - * :type:`nghttp3_read_data_callback` is a callback function invoked - * when the library asks an application to provide stream data for a - * stream denoted by |stream_id|. - * - * The library provides |vec| of length |veccnt| to the application. - * The application should fill data and its length to |vec|. It has - * to return the number of the filled objects. The application must - * retain data until they are safe to free. It is notified by - * :type:`nghttp3_acked_stream_data` callback. - * - * If this is the last data to send (or there is no data to send - * because all data have been sent already), set - * :enum:`NGHTTP3_DATA_FLAG_EOF` to |*pflags|. - * - * If the application is unable to provide data temporarily, return - * :enum:`NGHTTP3_ERR_WOULDBLOCK`. When it is ready to provide - * data, call `nghttp3_conn_resume_stream()`. - * - * The callback should return the number of objects in |vec| that the - * application filled if it succeeds, or - * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. - * - * TODO Add NGHTTP3_ERR_TEMPORAL_CALLBACK_FAILURE to reset just this - * stream. - */ -typedef nghttp3_ssize (*nghttp3_read_data_callback)( - nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt, - uint32_t *pflags, void *conn_user_data, void *stream_user_data); - -/** - * @struct - * - * :type:`nghttp3_data_reader` specifies the way how to generate - * request or response body. - */ -typedef struct { - /** - * read_data is a callback function to generate body. - */ - nghttp3_read_data_callback read_data; -} nghttp3_data_reader; - -/** - * @function - * - * `nghttp3_conn_submit_request` submits HTTP request header fields - * and body on the stream identified by |stream_id|. |stream_id| must - * be a client initiated bidirectional stream. Only client can submit - * HTTP request. |nva| of length |nvlen| specifies HTTP request - * header fields. |dr| specifies a request body. If there is no - * request body, specify NULL. If |dr| is NULL, it implies the end of - * stream. |stream_user_data| is an opaque pointer attached to the - * stream. - */ -NGHTTP3_EXTERN int nghttp3_conn_submit_request( - nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen, - const nghttp3_data_reader *dr, void *stream_user_data); - -/** - * @function - * - * `nghttp3_conn_submit_push_promise` submits push promise on the - * stream identified by |stream_id|. |stream_id| must be a client - * initiated bidirectional stream. Only server can submit push - * promise. On success, a push ID is assigned to |*ppush_id|. |nva| - * of length |nvlen| specifies HTTP request header fields. In order - * to submit HTTP response, first call - * `nghttp3_conn_bind_push_stream()` and then - * `nghttp3_conn_submit_response()`. - */ -NGHTTP3_EXTERN int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, - int64_t *ppush_id, - int64_t stream_id, - const nghttp3_nv *nva, - size_t nvlen); - -/** - * @function - * - * `nghttp3_conn_submit_info` submits HTTP non-final response header - * fields on the stream identified by |stream_id|. |nva| of length - * |nvlen| specifies HTTP response header fields. - */ -NGHTTP3_EXTERN int nghttp3_conn_submit_info(nghttp3_conn *conn, - int64_t stream_id, - const nghttp3_nv *nva, - size_t nvlen); - -/** - * @function - * - * `nghttp3_conn_submit_response` submits HTTP response header fields - * and body on the stream identified by |stream_id|. |nva| of length - * |nvlen| specifies HTTP response header fields. |dr| specifies a - * response body. If there is no response body, specify NULL. If - * |dr| is NULL, it implies the end of stream. - */ -NGHTTP3_EXTERN int nghttp3_conn_submit_response(nghttp3_conn *conn, - int64_t stream_id, - const nghttp3_nv *nva, - size_t nvlen, - const nghttp3_data_reader *dr); - -/** - * @function - * - * `nghttp3_conn_submit_trailers` submits HTTP trailer fields on the - * stream identified by |stream_id|. |nva| of length |nvlen| - * specifies HTTP trailer fields. Calling this function implies the - * end of stream. - */ -NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn, - int64_t stream_id, - const nghttp3_nv *nva, - size_t nvlen); - -/** - * @function - * - * `nghttp3_conn_bind_push_stream` binds the stream identified by - * |stream_id| to the push identified by |push_id|. |stream_id| must - * be a server initiated unidirectional stream. |push_id| must be - * obtained from `nghttp3_conn_submit_push_promise()`. To send - * response to this push, call `nghttp3_conn_submit_response()`. - */ -NGHTTP3_EXTERN int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, - int64_t push_id, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_cancel_push` cancels the push identified by - * |push_id|. It is not possible to cancel the push after the - * response stream opens. In that case, send RESET_STREAM (if |conn| - * is server) or STOP_SENDING (if |conn| is client) on the underlying - * QUIC stream. - */ -NGHTTP3_EXTERN int nghttp3_conn_cancel_push(nghttp3_conn *conn, - int64_t push_id); - -/** - * @function - * - * `nghttp3_conn_set_stream_user_data` sets |stream_user_data| to the - * stream identified by |stream_id|. - */ -NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, - int64_t stream_id, - void *stream_user_data); - -/** - * @function - * - * `nghttp3_conn_get_frame_payload_left` returns the number of bytes - * left to read current frame payload for a stream denoted by - * |stream_id|. If no such stream is found, it returns - * :enum:`NGHTTP3_ERR_STREAM_NOT_FOUND`. - */ -NGHTTP3_EXTERN int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, - int64_t stream_id); - -/** - * @macro - * - * :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level. - */ -#define NGHTTP3_DEFAULT_URGENCY 1 - -/** - * @macro - * - * :macro:`NGHTTP3_URGENCY_HIGH` is the highest urgency level. - */ -#define NGHTTP3_URGENCY_HIGH 0 - -/** - * @macro - * - * :macro:`NGHTTP3_URGENCY_LOW` is the lowest urgency level. - */ -#define NGHTTP3_URGENCY_LOW 7 - -/** - * @macro - * - * :macro:`NGHTTP3_URGENCY_LEVELS` is the number of urgency levels. - */ -#define NGHTTP3_URGENCY_LEVELS (NGHTTP3_URGENCY_LOW + 1) - -/** - * @struct - * - * :type:`nghttp3_pri` represents HTTP priority. - */ -typedef struct nghttp3_pri { - /** - * urgency is the urgency of a stream, it must be in - * [NGHTTP3_URGENCY_HIGH, NGHTTP3_URGENCY_LOW], inclusive, and 0 is - * the highest urgency. - */ - uint32_t urgency; - /** - * inc indicates that a content can be processed incrementally or - * not. If inc is 0, it cannot be processed incrementally. If inc - * is 1, it can be processed incrementally. Other value is not - * permitted. - */ - int inc; -} nghttp3_pri; - -/** - * @function - * - * `nghttp3_conn_get_stream_priority` stores stream priority of a - * stream denoted by |stream_id| into |*dest|. Only server can use - * this function. - * - * This function must not be called if |conn| is initialized as - * client. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_STREAM_NOT_FOUND` - * Stream not found. - */ -NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, - nghttp3_pri *dest, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_set_stream_priority` updates stream priority of a - * stream denoted by |stream_id| by the value pointed by |pri|. - * - * This function must not be called if |conn| is initialized as - * client. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_STREAM_NOT_FOUND` - * Stream not found. - * :enum:`NGHTTP3_ERR_NOMEM` - * Out of memory. - */ -NGHTTP3_EXTERN int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, - int64_t stream_id, - const nghttp3_pri *pri); - -/** - * @function - * - * `nghttp3_conn_is_remote_qpack_encoder_stream` returns nonzero if a - * stream denoted by |stream_id| is QPACK encoder stream of a remote - * endpoint. - */ -NGHTTP3_EXTERN int -nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt| - * elements. - */ -NGHTTP3_EXTERN size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); - -/** - * @function - * - * `nghttp3_vec_empty` returns nonzero if |vec| of |cnt| elements has - * 0 length data. - */ -NGHTTP3_EXTERN int nghttp3_vec_empty(const nghttp3_vec *vec, size_t cnt); - -/** - * @function - * - * `nghttp3_vec_consume` removes first |len| bytes from |*pvec| of - * |*pcnt| elements. The adjusted vector and number of elements are - * stored in |*pvec| and |*pcnt| respectively. - */ -NGHTTP3_EXTERN void nghttp3_vec_consume(nghttp3_vec **pvec, size_t *pcnt, - size_t len); - -/** - * @function - * - * `nghttp3_check_header_name` returns nonzero if HTTP header field - * name |name| of length |len| is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 - * - * Because this is a header field name in HTTP/3, the upper cased - * alphabet is treated as error. - */ -NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len); - -/** - * @function - * - * `nghttp3_check_header_value` returns nonzero if HTTP header field - * value |value| of length |len| is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 - */ -NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len); - -/** - * @function - * - * `nghttp3_http_parse_priority` parses priority HTTP header field - * stored in the buffer pointed by |value| of length |len|. If it - * successfully processed header field value, it stores the result - * into |*dest|. This function just overwrites what it sees in the - * header field value and does not initialize any field in |*dest|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP3_ERR_INVALID_ARGUMENT` - * The function could not parse the provided value. - */ -NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest, - const uint8_t *value, - size_t len); - -/** - * @macro - * - * The age of :type:`nghttp3_info` - */ -#define NGHTTP3_VERSION_AGE 1 - -/** - * @struct - * - * This struct is what `nghttp3_version()` returns. It holds - * information about the particular nghttp3 version. - */ -typedef struct { - /** - * Age of this struct. This instance of nghttp3 sets it to - * :macro:`NGHTTP3_VERSION_AGE` but a future version may bump it and - * add more struct fields at the bottom - */ - int age; - /** - * the :macro:`NGHTTP3_VERSION_NUM` number (since age ==1) - */ - int version_num; - /** - * points to the :macro:`NGHTTP3_VERSION` string (since age ==1) - */ - const char *version_str; - /* -------- the above fields all exist when age == 1 */ -} nghttp3_info; - -/** - * @function - * - * Returns a pointer to a nghttp3_info struct with version information - * about the run-time library in use. The |least_version| argument - * can be set to a 24 bit numerical value for the least accepted - * version number and if the condition is not met, this function will - * return a ``NULL``. Pass in 0 to skip the version checking. - */ -NGHTTP3_EXTERN nghttp3_info *nghttp3_version(int least_version); - -#ifdef __cplusplus -} -#endif - -#endif /* NGHTTP3_H */ diff --git a/deps/nghttp3/lib/includes/nghttp3/version.h b/deps/nghttp3/lib/includes/nghttp3/version.h deleted file mode 100644 index 69d29e9f140..00000000000 --- a/deps/nghttp3/lib/includes/nghttp3/version.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2016 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 NGHTTP3_VERSION_H -#define NGHTTP3_VERSION_H - -/** - * @macro - * - * Version number of the nghttp3 library release. - */ -#define NGHTTP3_VERSION "0.1.0-DEV" - -/** - * @macro - * - * Numerical representation of the version number of the nghttp3 - * library 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 NGHTTP3_VERSION_NUM 0x000100 - -#endif /* NGHTTP3_VERSION_H */ diff --git a/deps/nghttp3/lib/nghttp3_buf.c b/deps/nghttp3/lib/nghttp3_buf.c deleted file mode 100644 index aae075a73cc..00000000000 --- a/deps/nghttp3/lib/nghttp3_buf.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 "nghttp3_buf.h" - -void nghttp3_buf_init(nghttp3_buf *buf) { - buf->begin = buf->end = buf->pos = buf->last = NULL; -} - -void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len) { - buf->begin = buf->pos = buf->last = src; - buf->end = buf->begin + len; -} - -void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem) { - nghttp3_mem_free(mem, buf->begin); -} - -size_t nghttp3_buf_left(const nghttp3_buf *buf) { - return (size_t)(buf->end - buf->last); -} - -size_t nghttp3_buf_len(const nghttp3_buf *buf) { - return (size_t)(buf->last - buf->pos); -} - -size_t nghttp3_buf_cap(const nghttp3_buf *buf) { - return (size_t)(buf->end - 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) { - uint8_t *p; - nghttp3_ssize pos_offset, last_offset; - - if ((size_t)(buf->end - buf->begin) >= size) { - return 0; - } - - pos_offset = buf->pos - buf->begin; - last_offset = buf->last - buf->begin; - - p = nghttp3_mem_realloc(mem, buf->begin, size); - if (p == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - buf->begin = p; - buf->end = p + size; - buf->pos = p + pos_offset; - buf->last = p + last_offset; - - return 0; -} - -void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b) { - nghttp3_buf c = *a; - - *a = *b; - *b = c; -} - -void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, - nghttp3_buf_type type) { - tbuf->buf = *buf; - tbuf->type = type; -} diff --git a/deps/nghttp3/lib/nghttp3_buf.h b/deps/nghttp3/lib/nghttp3_buf.h deleted file mode 100644 index 493d81be315..00000000000 --- a/deps/nghttp3/lib/nghttp3_buf.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 NGHTTP3_BUF_H -#define NGHTTP3_BUF_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_mem.h" - -void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len); - -/* - * nghttp3_buf_cap returns the capacity of the buffer. In other - * words, it returns buf->end - buf->begin. - */ -size_t nghttp3_buf_cap(const nghttp3_buf *buf); - -int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem); - -/* - * nghttp3_buf_swap swaps |a| and |b|. - */ -void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b); - -typedef enum { - /* NGHTTP3_BUF_TYPE_PRIVATE indicates that memory is allocated for - this buffer only and should be freed after its use. */ - NGHTTP3_BUF_TYPE_PRIVATE, - /* NGHTTP3_BUF_TYPE_SHARED indicates that buffer points to shared - memory. */ - NGHTTP3_BUF_TYPE_SHARED, - /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a - memory which comes from outside of the library. */ - NGHTTP3_BUF_TYPE_ALIEN, -} nghttp3_buf_type; - -typedef struct { - nghttp3_buf buf; - nghttp3_buf_type type; -} nghttp3_typed_buf; - -void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, - nghttp3_buf_type type); - -void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf); - -#endif /* NGHTTP3_BUF_H */ diff --git a/deps/nghttp3/lib/nghttp3_conn.c b/deps/nghttp3/lib/nghttp3_conn.c deleted file mode 100644 index 3d10f393bf0..00000000000 --- a/deps/nghttp3/lib/nghttp3_conn.c +++ /dev/null @@ -1,3250 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_conn.h" - -#include <assert.h> -#include <string.h> -#include <stdio.h> - -#include "nghttp3_mem.h" -#include "nghttp3_macro.h" -#include "nghttp3_err.h" -#include "nghttp3_conv.h" -#include "nghttp3_http.h" - -/* - * conn_remote_stream_uni returns nonzero if |stream_id| is remote - * unidirectional stream ID. - */ -static int conn_remote_stream_uni(nghttp3_conn *conn, int64_t stream_id) { - if (conn->server) { - return (stream_id & 0x03) == 0x02; - } - return (stream_id & 0x03) == 0x03; -} - -static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.begin_headers) { - return 0; - } - - rv = conn->callbacks.begin_headers(conn, stream->node.nid.id, conn->user_data, - stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.end_headers) { - return 0; - } - - rv = conn->callbacks.end_headers(conn, stream->node.nid.id, conn->user_data, - stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_begin_trailers(nghttp3_conn *conn, - nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.begin_trailers) { - return 0; - } - - rv = conn->callbacks.begin_trailers(conn, stream->node.nid.id, - conn->user_data, stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.end_trailers) { - return 0; - } - - rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, conn->user_data, - stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_begin_push_promise(nghttp3_conn *conn, - nghttp3_stream *stream, - int64_t push_id) { - int rv; - - if (!conn->callbacks.begin_push_promise) { - return 0; - } - - rv = conn->callbacks.begin_push_promise(conn, stream->node.nid.id, push_id, - conn->user_data, stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_end_push_promise(nghttp3_conn *conn, - nghttp3_stream *stream, int64_t push_id) { - int rv; - - if (!conn->callbacks.end_push_promise) { - return 0; - } - - rv = conn->callbacks.end_push_promise(conn, stream->node.nid.id, push_id, - conn->user_data, stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.end_stream) { - return 0; - } - - rv = conn->callbacks.end_stream(conn, stream->node.nid.id, conn->user_data, - stream->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_cancel_push(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.cancel_push) { - return 0; - } - - rv = conn->callbacks.cancel_push( - conn, push_id, stream ? stream->node.nid.id : -1, conn->user_data, - stream ? stream->user_data : NULL); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_send_stop_sending(nghttp3_conn *conn, - nghttp3_stream *stream, - uint64_t app_error_code) { - int rv; - - if (!conn->callbacks.send_stop_sending) { - return 0; - } - - rv = conn->callbacks.send_stop_sending(conn, stream->node.nid.id, - app_error_code, conn->user_data, - stream->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_push_stream(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.push_stream) { - return 0; - } - - rv = conn->callbacks.push_stream(conn, push_id, stream->node.nid.id, - conn->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_deferred_consume(nghttp3_conn *conn, - nghttp3_stream *stream, - size_t nconsumed) { - int rv; - - if (nconsumed == 0 || !conn->callbacks.deferred_consume) { - return 0; - } - - rv = conn->callbacks.deferred_consume(conn, stream->node.nid.id, nconsumed, - conn->user_data, stream->user_data); - if (rv != 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 = - nghttp3_struct_of(lhsx, nghttp3_stream, qpack_blocked_pe); - nghttp3_stream *rhs = - nghttp3_struct_of(rhsx, nghttp3_stream, qpack_blocked_pe); - - return lhs->qpack_sctx.ricnt < rhs->qpack_sctx.ricnt; -} - -static int cycle_less(const nghttp3_pq_entry *lhsx, - const nghttp3_pq_entry *rhsx) { - const nghttp3_tnode *lhs = nghttp3_struct_of(lhsx, nghttp3_tnode, pe); - const nghttp3_tnode *rhs = nghttp3_struct_of(rhsx, nghttp3_tnode, pe); - - if (lhs->cycle == rhs->cycle) { - return lhs->seq < rhs->seq; - } - - return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP; -} - -static int conn_new(nghttp3_conn **pconn, int server, - const nghttp3_conn_callbacks *callbacks, - const nghttp3_conn_settings *settings, - const nghttp3_mem *mem, void *user_data) { - int rv; - nghttp3_conn *conn; - size_t i; - - conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn)); - if (conn == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - rv = nghttp3_map_init(&conn->streams, mem); - if (rv != 0) { - goto streams_init_fail; - } - - rv = nghttp3_map_init(&conn->pushes, mem); - if (rv != 0) { - goto pushes_init_fail; - } - - rv = nghttp3_qpack_decoder_init(&conn->qdec, - settings->qpack_max_table_capacity, - settings->qpack_blocked_streams, mem); - if (rv != 0) { - goto qdec_init_fail; - } - - rv = nghttp3_qpack_encoder_init(&conn->qenc, 0, 0, mem); - if (rv != 0) { - goto qenc_init_fail; - } - - nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem); - - for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { - nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem); - } - - rv = nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem); - if (rv != 0) { - goto remote_bidi_idtr_init_fail; - } - - rv = nghttp3_gaptr_init(&conn->remote.uni.push_idtr, mem); - if (rv != 0) { - goto push_idtr_init_fail; - } - - conn->callbacks = *callbacks; - conn->local.settings = *settings; - nghttp3_conn_settings_default(&conn->remote.settings); - conn->mem = mem; - conn->user_data = user_data; - conn->next_seq = 0; - conn->server = server; - - *pconn = conn; - - return 0; - -push_idtr_init_fail: - nghttp3_idtr_free(&conn->remote.bidi.idtr); -remote_bidi_idtr_init_fail: - nghttp3_qpack_encoder_free(&conn->qenc); -qenc_init_fail: - nghttp3_qpack_decoder_free(&conn->qdec); -qdec_init_fail: - nghttp3_map_free(&conn->pushes); -pushes_init_fail: - nghttp3_map_free(&conn->streams); -streams_init_fail: - nghttp3_mem_free(mem, conn); - - return rv; -} - -int nghttp3_conn_client_new(nghttp3_conn **pconn, - const nghttp3_conn_callbacks *callbacks, - const nghttp3_conn_settings *settings, - const nghttp3_mem *mem, void *user_data) { - int rv; - - rv = conn_new(pconn, /* server = */ 0, callbacks, settings, mem, user_data); - if (rv != 0) { - return rv; - } - - (*pconn)->remote.uni.unsent_max_pushes = settings->max_pushes; - - return 0; -} - -int nghttp3_conn_server_new(nghttp3_conn **pconn, - const nghttp3_conn_callbacks *callbacks, - const nghttp3_conn_settings *settings, - const nghttp3_mem *mem, void *user_data) { - int rv; - - rv = conn_new(pconn, /* server = */ 1, callbacks, settings, mem, user_data); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int free_push_promise(nghttp3_map_entry *ent, void *ptr) { - nghttp3_push_promise *pp = nghttp3_struct_of(ent, nghttp3_push_promise, me); - const nghttp3_mem *mem = ptr; - - nghttp3_push_promise_del(pp, mem); - - return 0; -} - -static int free_stream(nghttp3_map_entry *ent, void *ptr) { - nghttp3_stream *stream = nghttp3_struct_of(ent, nghttp3_stream, me); - - (void)ptr; - - nghttp3_stream_del(stream); - - return 0; -} - -void nghttp3_conn_del(nghttp3_conn *conn) { - size_t i; - - if (conn == NULL) { - return; - } - - nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem); - nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem); - - nghttp3_gaptr_free(&conn->remote.uni.push_idtr); - - nghttp3_idtr_free(&conn->remote.bidi.idtr); - - for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { - nghttp3_pq_free(&conn->sched[i].spq); - } - - nghttp3_pq_free(&conn->qpack_blocked_streams); - - nghttp3_qpack_encoder_free(&conn->qenc); - nghttp3_qpack_decoder_free(&conn->qdec); - - nghttp3_map_each_free(&conn->pushes, free_push_promise, (void *)conn->mem); - nghttp3_map_free(&conn->pushes); - - nghttp3_map_each_free(&conn->streams, free_stream, NULL); - nghttp3_map_free(&conn->streams); - - nghttp3_mem_free(conn->mem, conn); -} - -nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, - const uint8_t *src, size_t srclen, - int fin) { - nghttp3_stream *stream; - size_t bidi_nproc; - int rv; - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream == NULL) { - /* TODO Assert idtr */ - /* QUIC transport ensures that this is new stream. */ - if (conn->server) { - if (nghttp3_client_stream_bidi(stream_id)) { - rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); - assert(rv == 0); - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - } else { - /* unidirectional stream */ - if (srclen == 0 && fin) { - return 0; - } - - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - } - if (rv != 0) { - return rv; - } - - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; - stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; - } else if (nghttp3_stream_uni(stream_id)) { - if (srclen == 0 && fin) { - return 0; - } - - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - if (rv != 0) { - return rv; - } - - 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. */ - 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; - } - } - } else if (nghttp3_stream_uni(stream_id) && - stream->type == NGHTTP3_STREAM_TYPE_PUSH) { - if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) { - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; - stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; - } - } - - if (srclen == 0 && !fin) { - return 0; - } - - if (nghttp3_stream_uni(stream_id)) { - return nghttp3_conn_read_uni(conn, stream, src, srclen, fin); - } - - if (fin) { - stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; - } - return nghttp3_conn_read_bidi(conn, &bidi_nproc, stream, src, srclen, fin); -} - -static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream, - const uint8_t *src, size_t srclen, - int fin) { - nghttp3_stream_read_state *rstate = &stream->rstate; - nghttp3_varint_read_state *rvint = &rstate->rvint; - nghttp3_ssize nread; - int64_t stream_type; - - assert(srclen); - - nread = nghttp3_read_varint(rvint, src, srclen, fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - if (rvint->left) { - return nread; - } - - stream_type = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - switch (stream_type) { - case NGHTTP3_STREAM_TYPE_CONTROL: - if (conn->flags & NGHTTP3_CONN_FLAG_CONTROL_OPENED) { - return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; - } - conn->flags |= NGHTTP3_CONN_FLAG_CONTROL_OPENED; - stream->type = NGHTTP3_STREAM_TYPE_CONTROL; - rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE; - break; - case NGHTTP3_STREAM_TYPE_PUSH: - if (conn->server) { - return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; - } - stream->type = NGHTTP3_STREAM_TYPE_PUSH; - rstate->state = NGHTTP3_PUSH_STREAM_STATE_PUSH_ID; - break; - case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: - if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) { - return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; - } - conn->flags |= NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED; - stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER; - break; - case NGHTTP3_STREAM_TYPE_QPACK_DECODER: - if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED) { - return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; - } - conn->flags |= NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED; - stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER; - break; - default: - stream->type = NGHTTP3_STREAM_TYPE_UNKNOWN; - break; - } - - stream->flags |= NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED; - - return nread; -} - -static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream); - -nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, - const uint8_t *src, size_t srclen, - int fin) { - nghttp3_ssize nread = 0; - nghttp3_ssize nconsumed = 0; - size_t push_nproc; - int rv; - - assert(srclen || fin); - - if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) { - if (srclen == 0 && fin) { - /* Ignore stream if it is closed before reading stream header. - If it is closed while reading it, return error, making it - consistent in our code base. */ - if (stream->rstate.rvint.left) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - rv = conn_delete_stream(conn, stream); - assert(0 == rv); - - return 0; - } - nread = conn_read_type(conn, stream, src, srclen, fin); - if (nread < 0) { - return (int)nread; - } - if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) { - assert((size_t)nread == srclen); - return (nghttp3_ssize)srclen; - } - - src += nread; - srclen -= (size_t)nread; - - if (srclen == 0) { - return nread; - } - } - - switch (stream->type) { - case NGHTTP3_STREAM_TYPE_CONTROL: - if (fin) { - return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; - } - nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen); - break; - case NGHTTP3_STREAM_TYPE_PUSH: - if (fin) { - stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; - } - nconsumed = - nghttp3_conn_read_push(conn, &push_nproc, stream, src, srclen, fin); - break; - case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: - if (fin) { - return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; - } - nconsumed = nghttp3_conn_read_qpack_encoder(conn, src, srclen); - break; - case NGHTTP3_STREAM_TYPE_QPACK_DECODER: - if (fin) { - return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; - } - nconsumed = nghttp3_conn_read_qpack_decoder(conn, src, srclen); - break; - case NGHTTP3_STREAM_TYPE_UNKNOWN: - nconsumed = (nghttp3_ssize)srclen; - - rv = conn_call_send_stop_sending(conn, stream, - NGHTTP3_H3_STREAM_CREATION_ERROR); - if (rv != 0) { - return rv; - } - break; - default: - /* unreachable */ - assert(0); - } - - if (nconsumed < 0) { - return nconsumed; - } - - return nread + nconsumed; -} - -static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) { - return (int64_t)len >= rstate->left; -} - -nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, - nghttp3_stream *stream, - const uint8_t *src, size_t srclen) { - const uint8_t *p = src, *end = src + srclen; - int rv; - nghttp3_stream_read_state *rstate = &stream->rstate; - nghttp3_varint_read_state *rvint = &rstate->rvint; - nghttp3_ssize nread; - size_t nconsumed = 0; - int busy = 0; - size_t len; - - assert(srclen); - - for (; p != end || busy;) { - busy = 0; - switch (rstate->state) { - case NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - - rstate->fr.hd.type = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH; - if (p == end) { - break; - } - /* Fall through */ - case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - - rstate->left = rstate->fr.hd.length = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) { - if (rstate->fr.hd.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) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - - switch (rstate->fr.hd.type) { - case NGHTTP3_FRAME_CANCEL_PUSH: - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - rstate->state = NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH; - break; - case NGHTTP3_FRAME_SETTINGS: - /* SETTINGS frame might be empty. */ - if (rstate->left == 0) { - nghttp3_stream_read_state_reset(rstate); - break; - } - rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS; - break; - case NGHTTP3_FRAME_GOAWAY: - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - rstate->state = NGHTTP3_CTRL_STREAM_STATE_GOAWAY; - break; - case NGHTTP3_FRAME_MAX_PUSH_ID: - if (!conn->server) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID; - break; - case NGHTTP3_FRAME_DATA: - case NGHTTP3_FRAME_HEADERS: - case NGHTTP3_FRAME_PUSH_PROMISE: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - default: - /* TODO Handle reserved frame type */ - busy = 1; - rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; - break; - } - break; - case NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - assert(len > 0); - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - rstate->fr.cancel_push.push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (conn->server) { - rv = nghttp3_conn_on_server_cancel_push(conn, &rstate->fr.cancel_push); - } else { - rv = nghttp3_conn_on_client_cancel_push(conn, &rstate->fr.cancel_push); - } - if (rv != 0) { - return rv; - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_CTRL_STREAM_STATE_SETTINGS: - for (; p != end;) { - if (rstate->left == 0) { - nghttp3_stream_read_state_reset(rstate); - break; - } - /* Read Identifier */ - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - assert(len > 0); - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID; - return (nghttp3_ssize)nconsumed; - } - rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - /* Read Value */ - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - len -= (size_t)nread; - if (len == 0) { - rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; - break; - } - - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; - return (nghttp3_ssize)nconsumed; - } - rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - rv = - nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings); - if (rv != 0) { - return rv; - } - } - break; - case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - assert(len > 0); - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; - - if (p == end) { - return (nghttp3_ssize)nconsumed; - } - /* Fall through */ - case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - assert(len > 0); - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - rv = nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings); - if (rv != 0) { - return rv; - } - - if (rstate->left) { - rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS; - break; - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_CTRL_STREAM_STATE_GOAWAY: - /* TODO Not implemented yet */ - rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; - break; - case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID: - /* server side only */ - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - assert(len > 0); - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - - if (conn->local.uni.max_pushes > (uint64_t)rvint->acc) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - conn->local.uni.max_pushes = (uint64_t)rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - p += len; - nconsumed += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - return (nghttp3_ssize)nconsumed; - } - - nghttp3_stream_read_state_reset(rstate); - break; - default: - /* unreachable */ - assert(0); - } - } - - return (nghttp3_ssize)nconsumed; -} - -nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin) { - const uint8_t *p = src, *end = src ? src + srclen : src; - int rv; - nghttp3_stream_read_state *rstate = &stream->rstate; - nghttp3_varint_read_state *rvint = &rstate->rvint; - nghttp3_ssize nread; - size_t nconsumed = 0; - int busy = 0; - size_t len; - int64_t push_id; - - if (stream->pp && - (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { - *pnproc = srclen; - return (nghttp3_ssize)srclen; - } - - if (stream->flags & (NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED | - NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED)) { - *pnproc = 0; - - if (srclen == 0) { - return 0; - } - - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - return 0; - } - - for (; p != end || busy;) { - busy = 0; - switch (rstate->state) { - case NGHTTP3_PUSH_STREAM_STATE_PUSH_ID: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - rv = nghttp3_conn_on_stream_push_id(conn, stream, push_id); - if (rv != 0) { - if (rv == NGHTTP3_ERR_IGNORE_STREAM) { - rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_REST; - break; - } - return (nghttp3_ssize)rv; - } - - rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE; - - if (stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED) { - if (p != end) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (end == p) { - goto almost_done; - } - - /* Fall through */ - case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->fr.hd.type = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH; - if (p == end) { - goto almost_done; - } - /* Fall through */ - case NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->left = rstate->fr.hd.length = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - switch (rstate->fr.hd.type) { - case NGHTTP3_FRAME_DATA: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); - if (rv != 0) { - return rv; - } - /* DATA frame might be empty. */ - if (rstate->left == 0) { - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - rstate->state = NGHTTP3_PUSH_STREAM_STATE_DATA; - break; - case NGHTTP3_FRAME_HEADERS: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); - if (rv != 0) { - return rv; - } - if (rstate->left == 0) { - rv = nghttp3_stream_empty_headers_allowed(stream); - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = conn_call_begin_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_begin_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rstate->state = NGHTTP3_PUSH_STREAM_STATE_HEADERS; - break; - case NGHTTP3_FRAME_PUSH_PROMISE: - case NGHTTP3_FRAME_CANCEL_PUSH: - case NGHTTP3_FRAME_SETTINGS: - case NGHTTP3_FRAME_GOAWAY: - case NGHTTP3_FRAME_MAX_PUSH_ID: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - default: - /* TODO Handle reserved frame type */ - busy = 1; - rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME; - break; - } - break; - case NGHTTP3_PUSH_STREAM_STATE_DATA: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - rv = nghttp3_conn_on_data(conn, stream, p, len); - if (rv != 0) { - return rv; - } - - p += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_HEADERS: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = nghttp3_http_on_response_headers(&stream->rx.http); - if (rv != 0) { - return rv; - } - - rv = conn_call_end_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_end_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - p += len; - nconsumed += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: - nconsumed += (size_t)(end - p); - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - } - -almost_done: - if (fin) { - switch (rstate->state) { - case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: - if (rvint->left) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_MSG_END); - if (rv != 0) { - return rv; - } - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: - break; - default: - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - } - - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; -} - -static void conn_delete_push_promise(nghttp3_conn *conn, - nghttp3_push_promise *pp) { - int rv; - - rv = nghttp3_map_remove(&conn->pushes, (key_type)pp->node.nid.id); - assert(0 == rv); - - if (!conn->server && - !(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED)) { - ++conn->remote.uni.unsent_max_pushes; - } - - nghttp3_push_promise_del(pp, conn->mem); -} - -static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { - int rv; - - if (nghttp3_stream_bidi_or_push(stream)) { - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } - } - - rv = conn_call_deferred_consume(conn, stream, - nghttp3_stream_get_buffered_datalen(stream)); - if (rv != 0) { - return rv; - } - - if (conn->callbacks.stream_close) { - rv = conn->callbacks.stream_close(conn, stream->node.nid.id, - stream->error_code, conn->user_data, - stream->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - - rv = nghttp3_map_remove(&conn->streams, (key_type)stream->node.nid.id); - - assert(0 == rv); - - if (stream->pp) { - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - - conn_delete_push_promise(conn, stream->pp); - } - - nghttp3_stream_del(stream); - - return 0; -} - -static int conn_process_blocked_stream_data(nghttp3_conn *conn, - nghttp3_stream *stream) { - nghttp3_buf *buf; - size_t nproc; - nghttp3_ssize nconsumed; - int rv; - size_t len; - - for (;;) { - len = nghttp3_ringbuf_len(&stream->inq); - if (len == 0) { - break; - } - buf = nghttp3_ringbuf_get(&stream->inq, 0); - if (nghttp3_stream_uni(stream->node.nid.id)) { - nconsumed = nghttp3_conn_read_push( - conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), - len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); - } else { - nconsumed = nghttp3_conn_read_bidi( - conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), - len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); - } - if (nconsumed < 0) { - return (int)nconsumed; - } - - buf->pos += nproc; - - rv = conn_call_deferred_consume(conn, stream, (size_t)nconsumed); - if (rv != 0) { - return 0; - } - - if (nghttp3_buf_len(buf) == 0) { - nghttp3_buf_free(buf, stream->mem); - nghttp3_ringbuf_pop_front(&stream->inq); - } - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - break; - } - } - - if (!(stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) && - (stream->flags & NGHTTP3_STREAM_FLAG_CLOSED)) { - assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX); - - rv = conn_delete_stream(conn, stream); - if (rv != 0) { - return rv; - } - } - - return 0; -} - -nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, - const uint8_t *src, - size_t srclen) { - nghttp3_ssize nconsumed = - nghttp3_qpack_decoder_read_encoder(&conn->qdec, src, srclen); - nghttp3_stream *stream; - int rv; - - if (nconsumed < 0) { - return nconsumed; - } - - for (; !nghttp3_pq_empty(&conn->qpack_blocked_streams);) { - stream = nghttp3_struct_of(nghttp3_pq_top(&conn->qpack_blocked_streams), - nghttp3_stream, qpack_blocked_pe); - if (nghttp3_qpack_stream_context_get_ricnt(&stream->qpack_sctx) > - nghttp3_qpack_decoder_get_icnt(&conn->qdec)) { - break; - } - - nghttp3_conn_qpack_blocked_streams_pop(conn); - stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; - stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED; - - rv = conn_process_blocked_stream_data(conn, stream); - if (rv != 0) { - return rv; - } - } - - return nconsumed; -} - -nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, - const uint8_t *src, - size_t srclen) { - return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen); -} - -static int conn_update_stream_priority(nghttp3_conn *conn, - nghttp3_stream *stream, uint8_t pri) { - if (stream->node.pri == pri) { - return 0; - } - - nghttp3_conn_unschedule_stream(conn, stream); - - stream->node.pri = pri; - - assert(nghttp3_stream_bidi_or_push(stream)); - - if (nghttp3_stream_require_schedule(stream)) { - return nghttp3_conn_schedule_stream(conn, stream); - } - - return 0; -} - -nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin) { - const uint8_t *p = src, *end = src ? src + srclen : src; - int rv; - nghttp3_stream_read_state *rstate = &stream->rstate; - nghttp3_varint_read_state *rvint = &rstate->rvint; - nghttp3_ssize nread; - size_t nconsumed = 0; - int busy = 0; - size_t len; - nghttp3_push_promise *pp; - nghttp3_push_promise fake_pp = {{0}, {{0}, 0, {0}, 0, 0, 0}, {0}, NULL, -1, - 0}; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - *pnproc = 0; - - if (srclen == 0) { - return 0; - } - - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - return 0; - } - - for (; p != end || busy;) { - busy = 0; - switch (rstate->state) { - case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->fr.hd.type = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH; - if (p == end) { - goto almost_done; - } - /* Fall through */ - case NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->left = rstate->fr.hd.length = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - switch (rstate->fr.hd.type) { - case NGHTTP3_FRAME_DATA: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); - if (rv != 0) { - return rv; - } - /* DATA frame might be empty. */ - if (rstate->left == 0) { - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - rstate->state = NGHTTP3_REQ_STREAM_STATE_DATA; - break; - case NGHTTP3_FRAME_HEADERS: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); - if (rv != 0) { - return rv; - } - if (rstate->left == 0) { - rv = nghttp3_stream_empty_headers_allowed(stream); - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = conn_call_begin_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_begin_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS; - break; - case NGHTTP3_FRAME_PUSH_PROMISE: - if (conn->server) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - /* No stream->rx.hstate change with PUSH_PROMISE */ - - rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID; - break; - case NGHTTP3_FRAME_CANCEL_PUSH: - case NGHTTP3_FRAME_SETTINGS: - case NGHTTP3_FRAME_GOAWAY: - case NGHTTP3_FRAME_MAX_PUSH_ID: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - default: - /* TODO Handle reserved frame type */ - busy = 1; - rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_FRAME; - break; - } - break; - case NGHTTP3_REQ_STREAM_STATE_DATA: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - rv = nghttp3_conn_on_data(conn, stream, p, len); - if (rv != 0) { - return rv; - } - p += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), - (int64_t)len == rstate->left); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - goto almost_done; - } - - rstate->fr.push_promise.push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - rv = nghttp3_conn_on_push_promise_push_id( - conn, rstate->fr.push_promise.push_id, stream); - if (rv != 0) { - if (rv == NGHTTP3_ERR_IGNORE_PUSH_PROMISE) { - rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE; - if (p == end) { - goto almost_done; - } - break; - } - - return rv; - } - - rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE; - - if (p == end) { - goto almost_done; - } - /* Fall through */ - case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE: - pp = - nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); - - assert(pp); - - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, pp, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - rv = nghttp3_http_on_request_headers(&pp->http); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; - - rv = conn_call_end_push_promise(conn, stream, pp->node.nid.id); - if (rv != 0) { - return rv; - } - - if (!pp->stream && (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED)) { - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL) { - rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - } - - conn_delete_push_promise(conn, pp); - - nghttp3_stream_read_state_reset(rstate); - break; - } - - if (pp->stream) { - ++conn->remote.uni.unsent_max_pushes; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { - assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); - - rv = conn_call_push_stream(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - - pp->stream->flags &= - (uint16_t)~NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - rv = conn_process_blocked_stream_data(conn, pp->stream); - if (rv != 0) { - return rv; - } - } - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, &fake_pp, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_HEADERS: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - rv = nghttp3_http_on_request_headers(&stream->rx.http); - break; - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = nghttp3_http_on_response_headers(&stream->rx.http); - break; - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = 0; - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - /* Only server utilizes priority information to schedule - streams. */ - if (conn->server) { - rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri); - if (rv != 0) { - return rv; - } - } - /* fall through */ - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = conn_call_end_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_end_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - p += len; - nconsumed += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - nghttp3_stream_read_state_reset(rstate); - break; - } - } - -almost_done: - if (fin) { - switch (rstate->state) { - case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE: - if (rvint->left) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_MSG_END); - if (rv != 0) { - return rv; - } - rv = conn_call_end_stream(conn, stream); - if (rv != 0) { - return rv; - } - break; - default: - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - } - - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; -} - -int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, - const uint8_t *data, size_t datalen) { - int rv; - - rv = nghttp3_http_on_data_chunk(stream, datalen); - if (rv != 0) { - return rv; - } - - if (!conn->callbacks.recv_data) { - return 0; - } - - rv = conn->callbacks.recv_data(conn, stream->node.nid.id, data, datalen, - conn->user_data, stream->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; - nghttp3_push_promise *pp; - - if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp) { - if ((pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_BOUND) || - (pp->stream_id != -1 && pp->stream_id != stream->node.nid.id)) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } - if (pp->stream) { - assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); - /* Push unidirectional stream has already been received and - blocked */ - } else if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) { - /* We will call begin_push_promise callback even if push is - cancelled */ - } else { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - assert(pp->stream_id == -1); - - pp->stream_id = stream->node.nid.id; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; - } else if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)push_id, 1)) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } else { - rv = nghttp3_gaptr_push(push_idtr, (uint64_t)push_id, 1); - if (rv != 0) { - return rv; - } - - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); - if (rv != 0) { - return rv; - } - } - - rv = conn_call_begin_push_promise(conn, stream, push_id); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr) { - nghttp3_push_promise *pp; - nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; - int rv; - - if (conn->remote.uni.max_pushes <= (uint64_t)fr->push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, fr->push_id); - if (pp == NULL) { - if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)fr->push_id, 1)) { - /* push is already cancelled or server is misbehaving */ - return 0; - } - - /* We have not received PUSH_PROMISE yet */ - rv = nghttp3_gaptr_push(push_idtr, (uint64_t)fr->push_id, 1); - if (rv != 0) { - return rv; - } - - rv = nghttp3_conn_create_push_promise(conn, &pp, fr->push_id, NULL); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; - - /* cancel_push callback will be called after PUSH_PROMISE frame is - completely received. */ - - return 0; - } - - if (pp->stream) { - return 0; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { - rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - - conn_delete_push_promise(conn, pp); - - return 0; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; - - return 0; -} - -static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) { - uint32_t urgency = nghttp3_pri_uint8_urgency(tnode->pri); - - assert(urgency < NGHTTP3_URGENCY_LEVELS); - - return &conn->sched[urgency].spq; -} - -int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr) { - nghttp3_push_promise *pp; - nghttp3_stream *stream; - int rv; - - if (conn->local.uni.next_push_id <= fr->push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, fr->push_id); - if (pp == NULL) { - return 0; - } - - stream = pp->stream; - - rv = conn_call_cancel_push(conn, fr->push_id, stream); - if (rv != 0) { - return rv; - } - - if (stream) { - rv = nghttp3_conn_close_stream(conn, stream->node.nid.id, - NGHTTP3_H3_REQUEST_CANCELLED); - if (rv != 0) { - assert(NGHTTP3_ERR_STREAM_NOT_FOUND != rv); - return rv; - } - return 0; - } - - nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); - - conn_delete_push_promise(conn, pp); - - return 0; -} - -int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, - int64_t push_id) { - nghttp3_push_promise *pp; - int rv; - - if (nghttp3_gaptr_is_pushed(&conn->remote.uni.push_idtr, (uint64_t)push_id, - 1)) { - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp) { - if (pp->stream) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - pp->stream = stream; - stream->pp = pp; - - assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)); - assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL)); - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { - ++conn->remote.uni.unsent_max_pushes; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; - - return conn_call_push_stream(conn, push_id, stream); - } - - stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - return 0; - } - - /* Push ID has been received, but pp is gone. This means that - push is cancelled or server is misbehaving. We have no - information to distinguish the two, so just cancel QPACK stream - just in case, and ask application to send STOP_SENDING and - ignore all frames in this stream. */ - rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); - if (rv != 0) { - return rv; - } - rv = - conn_call_send_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_CANCELLED); - if (rv != 0) { - return rv; - } - return NGHTTP3_ERR_IGNORE_STREAM; - } - - if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - rv = nghttp3_gaptr_push(&conn->remote.uni.push_idtr, (uint64_t)push_id, 1); - if (rv != 0) { - return rv; - } - - /* Don't know the associated stream of PUSH_PROMISE. It doesn't - matter because client sends nothing to this stream. */ - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, NULL); - if (rv != 0) { - return rv; - } - - pp->stream = stream; - stream->pp = pp; - stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - return 0; -} - -static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, - nghttp3_stream *stream, - nghttp3_push_promise *pp, - const uint8_t *src, size_t srclen, - int fin) { - nghttp3_ssize nread; - int rv; - nghttp3_qpack_decoder *qdec = &conn->qdec; - nghttp3_qpack_nv nv; - uint8_t flags; - nghttp3_buf buf; - nghttp3_recv_header recv_header = NULL; - nghttp3_http_state *http; - int request = 0; - int trailers = 0; - int ignore_pp = 0; - - if (pp) { - request = 1; - ignore_pp = pp->stream_id != stream->node.nid.id; - if (ignore_pp) { - http = NULL; - } else { - http = &pp->http; - } - } else { - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - request = 1; - /* Fall through */ - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - recv_header = conn->callbacks.recv_header; - break; - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - request = 1; - /* Fall through */ - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - trailers = 1; - recv_header = conn->callbacks.recv_trailer; - break; - default: - /* Unreachable */ - assert(0); - } - http = &stream->rx.http; - } - - nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen); - buf.last = buf.end; - - for (;;) { - nread = nghttp3_qpack_decoder_read_request(qdec, &stream->qpack_sctx, &nv, - &flags, buf.pos, - nghttp3_buf_len(&buf), fin); - - if (nread < 0) { - return (int)nread; - } - - buf.pos += nread; - - if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) { - if (conn->local.settings.qpack_blocked_streams <= - nghttp3_pq_size(&conn->qpack_blocked_streams)) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - - stream->flags |= NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED; - rv = nghttp3_conn_qpack_blocked_streams_push(conn, stream); - if (rv != 0) { - return rv; - } - break; - } - - if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) { - nghttp3_qpack_stream_context_reset(&stream->qpack_sctx); - break; - } - - if (nread == 0) { - break; - } - - if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) { - if (ignore_pp) { - nghttp3_rcbuf_decref(nv.name); - nghttp3_rcbuf_decref(nv.value); - - continue; - } - - assert(http); - - rv = nghttp3_http_on_header(http, stream->rstate.fr.hd.type, &nv, request, - trailers); - switch (rv) { - case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: - break; - case NGHTTP3_ERR_REMOVE_HTTP_HEADER: - rv = 0; - break; - case 0: - if (pp) { - if (conn->callbacks.recv_push_promise) { - rv = conn->callbacks.recv_push_promise( - conn, stream->node.nid.id, pp->node.nid.id, nv.token, nv.name, - nv.value, nv.flags, conn->user_data, stream->user_data); - } - break; - } - if (recv_header) { - rv = recv_header(conn, stream->node.nid.id, nv.token, nv.name, - nv.value, nv.flags, conn->user_data, - stream->user_data); - } - break; - default: - /* Unreachable */ - assert(0); - } - - nghttp3_rcbuf_decref(nv.name); - nghttp3_rcbuf_decref(nv.value); - - if (rv != 0) { - return rv; - } - } - } - - return buf.pos - src; -} - -nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, - nghttp3_stream *stream, - nghttp3_push_promise *pp, - const uint8_t *src, size_t srclen, - int fin) { - if (srclen == 0 && !fin) { - return 0; - } - - return conn_decode_headers(conn, stream, pp, src, srclen, fin); -} - -int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, - const nghttp3_frame_settings *fr) { - const nghttp3_settings_entry *ent = &fr->iv[0]; - nghttp3_conn_settings *dest = &conn->remote.settings; - int rv; - size_t max_table_capacity = SIZE_MAX; - size_t max_blocked_streams = SIZE_MAX; - - /* TODO Check for duplicates */ - switch (ent->id) { - case NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE: - dest->max_field_section_size = ent->value; - break; - case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY: - dest->qpack_max_table_capacity = (size_t)ent->value; - max_table_capacity = - nghttp3_min(max_table_capacity, dest->qpack_max_table_capacity); - rv = nghttp3_qpack_encoder_set_hard_max_dtable_size(&conn->qenc, - max_table_capacity); - if (rv != 0) { - return rv; - } - rv = nghttp3_qpack_encoder_set_max_dtable_size(&conn->qenc, - max_table_capacity); - if (rv != 0) { - return rv; - } - break; - case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS: - dest->qpack_blocked_streams = (size_t)ent->value; - max_blocked_streams = - nghttp3_min(max_blocked_streams, dest->qpack_blocked_streams); - rv = - nghttp3_qpack_encoder_set_max_blocked(&conn->qenc, max_blocked_streams); - if (rv != 0) { - return rv; - } - break; - default: - /* Ignore unknown settings ID */ - break; - } - - return 0; -} - -static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id, - size_t datalen, void *user_data) { - nghttp3_conn *conn = stream->conn; - int rv; - - if (!conn->callbacks.acked_stream_data) { - return 0; - } - - rv = conn->callbacks.acked_stream_data(conn, stream_id, datalen, - conn->user_data, user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, - int64_t stream_id) { - nghttp3_stream *stream; - int rv; - nghttp3_stream_callbacks callbacks = { - conn_stream_acked_data, - }; - - rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks, - conn->mem); - if (rv != 0) { - return rv; - } - - stream->conn = conn; - - rv = nghttp3_map_insert(&conn->streams, &stream->me); - if (rv != 0) { - nghttp3_stream_del(stream); - return rv; - } - - ++conn->next_seq; - *pstream = stream; - - return 0; -} - -int nghttp3_conn_create_push_promise(nghttp3_conn *conn, - nghttp3_push_promise **ppp, - int64_t push_id, - nghttp3_tnode *assoc_tnode) { - nghttp3_push_promise *pp; - int rv; - - rv = nghttp3_push_promise_new(&pp, push_id, conn->next_seq, assoc_tnode, - conn->mem); - if (rv != 0) { - return rv; - } - - rv = nghttp3_map_insert(&conn->pushes, &pp->me); - if (rv != 0) { - nghttp3_push_promise_del(pp, conn->mem); - return rv; - } - - ++conn->next_seq; - *ppp = pp; - - return 0; -} - -nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, - int64_t stream_id) { - nghttp3_map_entry *me; - - me = nghttp3_map_find(&conn->streams, (key_type)stream_id); - if (me == NULL) { - return NULL; - } - - return nghttp3_struct_of(me, nghttp3_stream, me); -} - -nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, - int64_t push_id) { - nghttp3_map_entry *me; - - me = nghttp3_map_find(&conn->pushes, (key_type)push_id); - if (me == NULL) { - return NULL; - } - - return nghttp3_struct_of(me, nghttp3_push_promise, me); -} - -int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { - nghttp3_stream *stream; - nghttp3_frame_entry frent; - int rv; - - assert(!conn->server || nghttp3_server_stream_uni(stream_id)); - assert(conn->server || nghttp3_client_stream_uni(stream_id)); - - if (conn->tx.ctrl) { - return NGHTTP3_ERR_INVALID_STATE; - } - - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - if (rv != 0) { - return rv; - } - - stream->type = NGHTTP3_STREAM_TYPE_CONTROL; - - conn->tx.ctrl = stream; - - rv = nghttp3_stream_write_stream_type(stream); - if (rv != 0) { - return rv; - } - - frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS; - frent.aux.settings.local_settings = &conn->local.settings; - - return nghttp3_stream_frq_add(stream, &frent); -} - -int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, - int64_t qdec_stream_id) { - nghttp3_stream *stream; - int rv; - - 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)); - assert(conn->server || nghttp3_client_stream_uni(qdec_stream_id)); - - if (conn->tx.qenc || conn->tx.qdec) { - return NGHTTP3_ERR_INVALID_STATE; - } - - rv = nghttp3_conn_create_stream(conn, &stream, qenc_stream_id); - if (rv != 0) { - return rv; - } - - stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER; - - conn->tx.qenc = stream; - - rv = nghttp3_stream_write_stream_type(stream); - if (rv != 0) { - return rv; - } - - rv = nghttp3_conn_create_stream(conn, &stream, qdec_stream_id); - if (rv != 0) { - return rv; - } - - stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER; - - conn->tx.qdec = stream; - - return nghttp3_stream_write_stream_type(stream); -} - -static nghttp3_ssize conn_writev_stream(nghttp3_conn *conn, int64_t *pstream_id, - int *pfin, nghttp3_vec *vec, - size_t veccnt, nghttp3_stream *stream) { - int rv; - nghttp3_ssize n; - - assert(veccnt > 0); - - /* If stream is blocked by read callback, don't attempt to fill - more. */ - if (!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)) { - rv = nghttp3_stream_fill_outq(stream); - if (rv != 0) { - return rv; - } - } - - if (!nghttp3_stream_uni(stream->node.nid.id) && conn->tx.qenc && - !nghttp3_stream_is_blocked(conn->tx.qenc)) { - n = nghttp3_stream_writev(conn->tx.qenc, pfin, vec, veccnt); - if (n < 0) { - return n; - } - if (n) { - *pstream_id = conn->tx.qenc->node.nid.id; - return n; - } - } - - n = nghttp3_stream_writev(stream, pfin, vec, veccnt); - if (n < 0) { - return n; - } - /* We might just want to write stream fin without sending any stream - data. */ - if (n == 0 && *pfin == 0) { - return 0; - } - - *pstream_id = stream->node.nid.id; - - return n; -} - -nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, - int64_t *pstream_id, int *pfin, - nghttp3_vec *vec, size_t veccnt) { - nghttp3_ssize ncnt; - nghttp3_stream *stream; - int rv; - - *pstream_id = -1; - *pfin = 0; - - if (veccnt == 0) { - return 0; - } - - if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) { - if (!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED) && - conn->remote.uni.unsent_max_pushes > conn->remote.uni.max_pushes) { - rv = nghttp3_conn_submit_max_push_id(conn); - if (rv != 0) { - return rv; - } - } - - ncnt = - conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl); - if (ncnt) { - return ncnt; - } - } - - if (conn->tx.qdec && !nghttp3_stream_is_blocked(conn->tx.qdec)) { - rv = nghttp3_stream_write_qpack_decoder_stream(conn->tx.qdec); - if (rv != 0) { - return rv; - } - - ncnt = - conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qdec); - if (ncnt) { - return ncnt; - } - } - - if (conn->tx.qenc && !nghttp3_stream_is_blocked(conn->tx.qenc)) { - ncnt = - conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qenc); - if (ncnt) { - return ncnt; - } - } - - stream = nghttp3_conn_get_next_tx_stream(conn); - if (stream == NULL) { - return 0; - } - - ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, stream); - if (ncnt < 0) { - return ncnt; - } - - if (nghttp3_stream_bidi_or_push(stream) && - !nghttp3_stream_require_schedule(stream)) { - nghttp3_conn_unschedule_stream(conn, stream); - } - - return ncnt; -} - -nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) { - size_t i; - nghttp3_tnode *tnode; - nghttp3_pq *pq; - - for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { - pq = &conn->sched[i].spq; - if (nghttp3_pq_empty(pq)) { - continue; - } - - tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); - - if (tnode->nid.type == NGHTTP3_NODE_ID_TYPE_PUSH) { - return nghttp3_struct_of(tnode, nghttp3_push_promise, node)->stream; - } - - return nghttp3_struct_of(tnode, nghttp3_stream, node); - } - - return NULL; -} - -int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, - size_t n) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - int rv; - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - rv = nghttp3_stream_add_outq_offset(stream, n); - if (rv != 0) { - return rv; - } - - stream->unscheduled_nwrite += n; - - if (!nghttp3_stream_bidi_or_push(stream)) { - return 0; - } - - if (!nghttp3_stream_require_schedule(stream)) { - nghttp3_conn_unschedule_stream(conn, stream); - return 0; - } - - if (stream->unscheduled_nwrite < NGHTTP3_STREAM_MIN_WRITELEN) { - return 0; - } - - return nghttp3_conn_schedule_stream(conn, stream); -} - -int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id, - uint64_t n) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - return nghttp3_stream_add_ack_offset(stream, n); -} - -static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, - const nghttp3_nv *nva, size_t nvlen, - const nghttp3_data_reader *dr) { - int rv; - nghttp3_nv *nnva; - 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.headers.nva = nnva; - frent.fr.headers.nvlen = nvlen; - - rv = nghttp3_stream_frq_add(stream, &frent); - if (rv != 0) { - nghttp3_nva_del(nnva, conn->mem); - return rv; - } - - if (dr) { - frent.fr.hd.type = NGHTTP3_FRAME_DATA; - frent.aux.data.dr = *dr; - - rv = nghttp3_stream_frq_add(stream, &frent); - if (rv != 0) { - return rv; - } - } - - if (nghttp3_stream_require_schedule(stream)) { - return nghttp3_conn_schedule_stream(conn, stream); - } - - return 0; -} - -static nghttp3_tnode *stream_get_dependency_node(nghttp3_stream *stream) { - if (stream->pp) { - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - return &stream->pp->node; - } - - return &stream->node; -} - -int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { - /* Assume that stream stays on the same urgency level */ - int rv; - - rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream), - conn_get_sched_pq(conn, &stream->node), - stream->unscheduled_nwrite); - if (rv != 0) { - return rv; - } - - stream->unscheduled_nwrite = 0; - - return 0; -} - -int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, - nghttp3_stream *stream) { - if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) { - return 0; - } - - return nghttp3_conn_schedule_stream(conn, stream); -} - -void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, - nghttp3_stream *stream) { - nghttp3_tnode_unschedule(stream_get_dependency_node(stream), - conn_get_sched_pq(conn, &stream->node)); -} - -int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, - const nghttp3_nv *nva, size_t nvlen, - const nghttp3_data_reader *dr, - void *stream_user_data) { - nghttp3_stream *stream; - int rv; - - assert(!conn->server); - assert(conn->tx.qenc); - - 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; - } - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream != NULL) { - return NGHTTP3_ERR_STREAM_IN_USE; - } - - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - if (rv != 0) { - return rv; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; - stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_END; - stream->user_data = stream_user_data; - - nghttp3_http_record_request_method(stream, nva, nvlen); - - if (dr == NULL) { - stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; - } - - return conn_submit_headers_data(conn, stream, nva, nvlen, dr); -} - -int nghttp3_conn_submit_info(nghttp3_conn *conn, int64_t stream_id, - const nghttp3_nv *nva, size_t nvlen) { - nghttp3_stream *stream; - - /* TODO Verify that it is allowed to send info (non-final response) - now. */ - assert(conn->server); - assert(conn->tx.qenc); - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); -} - -int nghttp3_conn_submit_response(nghttp3_conn *conn, int64_t stream_id, - const nghttp3_nv *nva, size_t nvlen, - const nghttp3_data_reader *dr) { - nghttp3_stream *stream; - - /* TODO Verify that it is allowed to send response now. */ - assert(conn->server); - assert(conn->tx.qenc); - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - if (dr == NULL) { - stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; - } - - return conn_submit_headers_data(conn, stream, nva, nvlen, dr); -} - -int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id, - const nghttp3_nv *nva, size_t nvlen) { - nghttp3_stream *stream; - - /* TODO Verify that it is allowed to send trailer now. */ - assert(conn->tx.qenc); - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - if (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM) { - return NGHTTP3_ERR_INVALID_STATE; - } - - stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; - - return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); -} - -int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, int64_t *ppush_id, - int64_t stream_id, const nghttp3_nv *nva, - size_t nvlen) { - nghttp3_stream *stream; - int rv; - nghttp3_nv *nnva; - nghttp3_frame_entry frent; - int64_t push_id; - nghttp3_push_promise *pp; - - assert(conn->server); - assert(conn->tx.qenc); - assert(nghttp3_client_stream_bidi(stream_id)); - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - if (conn->local.uni.max_pushes <= (uint64_t)conn->local.uni.next_push_id) { - return NGHTTP3_ERR_PUSH_ID_BLOCKED; - } - - push_id = conn->local.uni.next_push_id; - - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); - if (rv != 0) { - return rv; - } - - ++conn->local.uni.next_push_id; - - rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); - if (rv != 0) { - return rv; - } - - frent.fr.hd.type = NGHTTP3_FRAME_PUSH_PROMISE; - frent.fr.push_promise.push_id = push_id; - frent.fr.push_promise.nva = nnva; - frent.fr.push_promise.nvlen = nvlen; - - rv = nghttp3_stream_frq_add(stream, &frent); - if (rv != 0) { - nghttp3_nva_del(nnva, conn->mem); - return rv; - } - - *ppush_id = push_id; - - if (nghttp3_stream_require_schedule(stream)) { - return nghttp3_conn_schedule_stream(conn, stream); - } - - return 0; -} - -int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id) { - nghttp3_push_promise *pp; - nghttp3_stream *stream; - int rv; - - assert(conn->server); - assert(nghttp3_server_stream_uni(stream_id)); - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - assert(NULL == nghttp3_conn_find_stream(conn, stream_id)); - - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - if (rv != 0) { - return rv; - } - - stream->type = NGHTTP3_STREAM_TYPE_PUSH; - stream->pp = pp; - - pp->stream = stream; - - rv = nghttp3_stream_write_stream_type_push_id(stream); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp3_conn_cancel_push(nghttp3_conn *conn, int64_t push_id) { - if (conn->server) { - return nghttp3_conn_server_cancel_push(conn, push_id); - } - return nghttp3_conn_client_cancel_push(conn, push_id); -} - -int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id) { - nghttp3_push_promise *pp; - nghttp3_frame_entry frent; - int rv; - - assert(conn->tx.ctrl); - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = push_id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; - } - - if (pp->stream) { - /* CANCEL_PUSH will be sent, but it does not affect pushed stream. - Application should explicitly shutdown the stream. */ - return NGHTTP3_ERR_TOO_LATE; - } - - nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); - - conn_delete_push_promise(conn, pp); - - return 0; -} - -int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id) { - nghttp3_push_promise *pp; - nghttp3_frame_entry frent; - int rv; - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (pp->stream) { - return NGHTTP3_ERR_TOO_LATE; - } - - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = push_id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { - conn_delete_push_promise(conn, pp); - return 0; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; - - return 0; -} - -int nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED; - stream->unscheduled_nwrite = 0; - - if (nghttp3_stream_bidi_or_push(stream)) { - nghttp3_conn_unschedule_stream(conn, stream); - } - - return 0; -} - -int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED; - - if (nghttp3_stream_bidi_or_push(stream) && - nghttp3_stream_require_schedule(stream)) { - return nghttp3_conn_ensure_stream_scheduled(conn, stream); - } - - return 0; -} - -int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; - - if (nghttp3_stream_bidi_or_push(stream) && - nghttp3_stream_require_schedule(stream)) { - return nghttp3_conn_ensure_stream_scheduled(conn, stream); - } - - return 0; -} - -int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - if (nghttp3_stream_uni(stream_id) && - stream->type != NGHTTP3_STREAM_TYPE_PUSH && - stream->type != NGHTTP3_STREAM_TYPE_UNKNOWN) { - return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; - } - - stream->error_code = app_error_code; - - nghttp3_conn_unschedule_stream(conn, stream); - - if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX && - (conn->server || !stream->pp || - (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED))) { - return conn_delete_stream(conn, stream); - } - - stream->flags |= NGHTTP3_STREAM_FLAG_CLOSED; - return 0; -} - -int nghttp3_conn_reset_stream(nghttp3_conn *conn, int64_t stream_id) { - nghttp3_stream *stream; - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream) { - stream->flags |= NGHTTP3_STREAM_FLAG_RESET; - } - return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id); -} - -int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, - nghttp3_stream *stream) { - assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX); - - return nghttp3_pq_push(&conn->qpack_blocked_streams, - &stream->qpack_blocked_pe); -} - -void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn) { - assert(!nghttp3_pq_empty(&conn->qpack_blocked_streams)); - nghttp3_pq_pop(&conn->qpack_blocked_streams); -} - -void nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn, - uint64_t max_streams) { - assert(conn->server); - assert(conn->remote.bidi.max_client_streams <= max_streams); - - conn->remote.bidi.max_client_streams = max_streams; -} - -void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, - size_t max_concurrent_streams) { - nghttp3_qpack_decoder_set_max_concurrent_streams(&conn->qdec, - max_concurrent_streams); -} - -int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn) { - nghttp3_frame_entry frent; - int rv; - - assert(conn->tx.ctrl); - assert(!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED)); - - frent.fr.hd.type = NGHTTP3_FRAME_MAX_PUSH_ID; - /* The acutal push_id is set when frame is serialized */ - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - conn->flags |= NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; - - return 0; -} - -int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id, - void *stream_user_data) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - stream->user_data = stream_user_data; - - return 0; -} - -int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, - int64_t stream_id) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - return stream->rstate.left; -} - -int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, nghttp3_pri *dest, - int64_t stream_id) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - assert(conn->server); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - dest->urgency = nghttp3_pri_uint8_urgency(stream->rx.http.pri); - dest->inc = nghttp3_pri_uint8_inc(stream->rx.http.pri); - - return 0; -} - -int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, int64_t stream_id, - const nghttp3_pri *pri) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); - - assert(conn->server); - assert(pri->urgency < NGHTTP3_URGENCY_LEVELS); - assert(pri->inc == 0 || pri->inc == 1); - - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - stream->rx.http.pri = nghttp3_pri_to_uint8(pri); - - return conn_update_stream_priority(conn, stream, stream->rx.http.pri); -} - -int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, - int64_t stream_id) { - nghttp3_stream *stream; - - if (!conn_remote_stream_uni(conn, stream_id)) { - return 0; - } - - stream = nghttp3_conn_find_stream(conn, stream_id); - return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER; -} - -void nghttp3_conn_settings_default(nghttp3_conn_settings *settings) { - memset(settings, 0, sizeof(nghttp3_conn_settings)); - settings->max_field_section_size = NGHTTP3_VARINT_MAX; -} - -int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, - uint64_t seq, nghttp3_tnode *assoc_tnode, - const nghttp3_mem *mem) { - nghttp3_push_promise *pp; - nghttp3_node_id nid; - - pp = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_push_promise)); - if (pp == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - nghttp3_tnode_init( - &pp->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PUSH, push_id), - seq, NGHTTP3_DEFAULT_URGENCY); - - pp->me.key = (key_type)push_id; - pp->node.nid.id = push_id; - pp->http.status_code = -1; - pp->http.content_length = -1; - - if (assoc_tnode) { - assert(assoc_tnode->nid.type == NGHTTP3_NODE_ID_TYPE_STREAM); - - pp->stream_id = assoc_tnode->nid.id; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; - } else { - pp->stream_id = -1; - } - - *ppp = pp; - - return 0; -} - -void nghttp3_push_promise_del(nghttp3_push_promise *pp, - const nghttp3_mem *mem) { - if (pp == NULL) { - return; - } - - nghttp3_tnode_free(&pp->node); - - nghttp3_mem_free(mem, pp); -} diff --git a/deps/nghttp3/lib/nghttp3_conn.h b/deps/nghttp3/lib/nghttp3_conn.h deleted file mode 100644 index 720b6102426..00000000000 --- a/deps/nghttp3/lib/nghttp3_conn.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_CONN_H -#define NGHTTP3_CONN_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_stream.h" -#include "nghttp3_map.h" -#include "nghttp3_qpack.h" -#include "nghttp3_tnode.h" -#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 - -/* NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS is the maximum number of - blocked streams for QPACK encoder. */ -#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100 - -typedef enum { - NGHTTP3_PUSH_PROMISE_FLAG_NONE = 0x00, - /* NGHTTP3_PUSH_PROMISE_FLAG_RECVED is set when PUSH_PROMISE is - completely received. */ - NGHTTP3_PUSH_PROMISE_FLAG_RECVED = 0x01, - /* NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL is set when push is - cancelled by server before receiving PUSH_PROMISE completely. - This flag should have no effect if push stream has already - opened. */ - NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL = 0x02, - /* NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL is set when push is - canceled by the local endpoint. */ - NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL = 0x04, - NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED = NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL | - NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL, - /* NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED indicates that - unsent_max_pushes has been updated for this push ID. */ - NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED = 0x08, - /* NGHTTP3_PUSH_PROMISE_FLAG_BOUND is set if nghttp3_push_promise - object is bound to a stream where PUSH_PROMISE frame is received - first. nghttp3_push_promise object might be created before - receiving any PUSH_PROMISE when pushed stream is received before - it.*/ - NGHTTP3_PUSH_PROMISE_FLAG_BOUND = 0x10, -} nghttp3_push_promise_flag; - -struct nghttp3_push_promise; -typedef struct nghttp3_push_promise nghttp3_push_promise; - -struct nghttp3_push_promise { - nghttp3_map_entry me; - nghttp3_tnode node; - nghttp3_http_state http; - /* stream is server initiated unidirectional stream which fulfils - the push promise. */ - nghttp3_stream *stream; - /* stream_id is the stream ID where this PUSH_PROMISE is first - received. PUSH_PROMISE with same push ID is allowed to be sent - in the multiple streams. We ignore those duplicated PUSH_PROMISE - entirely because we don't see any value to add extra cost of - processing for it. Even ignoring those frame is not yet easy - because we have to decode the header blocks. Server push is - overly complex and there is no good use case after all. */ - int64_t stream_id; - /* flags is bitwise OR of zero or more of - nghttp3_push_promise_flag. */ - uint16_t flags; -}; - -typedef enum { - NGHTTP3_CONN_FLAG_NONE = 0x0000, - NGHTTP3_CONN_FLAG_SETTINGS_RECVED = 0x0001, - NGHTTP3_CONN_FLAG_CONTROL_OPENED = 0x0002, - NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED = 0x0004, - NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED = 0x0008, - /* NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED indicates that MAX_PUSH_ID - has been queued to control stream. */ - NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED = 0x0010, -} nghttp3_conn_flag; - -struct nghttp3_conn { - nghttp3_conn_callbacks callbacks; - nghttp3_map streams; - nghttp3_map pushes; - nghttp3_qpack_decoder qdec; - nghttp3_qpack_encoder qenc; - nghttp3_pq qpack_blocked_streams; - struct { - nghttp3_pq spq; - } sched[NGHTTP3_URGENCY_LEVELS]; - const nghttp3_mem *mem; - void *user_data; - int server; - uint16_t flags; - uint64_t next_seq; - - struct { - nghttp3_conn_settings settings; - struct { - /* max_pushes is the number of push IDs that local endpoint can - issue. This field is used by server only. */ - uint64_t max_pushes; - /* next_push_id is the next push ID server uses. This field is - used by server only. */ - int64_t next_push_id; - } uni; - } local; - - struct { - struct { - nghttp3_idtr idtr; - /* max_client_streams is the cumulative number of client - initiated bidirectional stream ID the remote endpoint can - issue. This field is used on server side only. */ - uint64_t max_client_streams; - } bidi; - struct { - /* push_idtr tracks which push ID has been used by remote - server. This field is used by client only. */ - nghttp3_gaptr push_idtr; - /* unsent_max_pushes is the maximum number of push which the local - endpoint can accept. This limit is not yet notified to the - remote endpoint. This field is used by client only. */ - uint64_t unsent_max_pushes; - /* max_push is the maximum number of push which the local - endpoint can accept. This field is used by client only. */ - uint64_t max_pushes; - } uni; - nghttp3_conn_settings settings; - } remote; - - struct { - struct { - nghttp3_buf rbuf; - nghttp3_buf ebuf; - } qpack; - nghttp3_stream *ctrl; - nghttp3_stream *qenc; - nghttp3_stream *qdec; - } tx; -}; - -nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id); - -nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, - int64_t push_id); - -int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, - int64_t stream_id); - -int nghttp3_conn_create_push_promise(nghttp3_conn *conn, - nghttp3_push_promise **ppp, - int64_t push_id, - nghttp3_tnode *assoc_tnode); - -nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin); - -nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, - const uint8_t *src, size_t srclen, int fin); - -nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, - nghttp3_stream *stream, - const uint8_t *src, size_t srclen); - -nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin); - -nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, - const uint8_t *src, - size_t srclen); - -nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, - const uint8_t *src, - size_t srclen); - -int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream); - -int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr); - -int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr); - -int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, - int64_t push_id); - -int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, - const uint8_t *data, size_t datalen); - -nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, - nghttp3_stream *stream, - nghttp3_push_promise *pp, - const uint8_t *data, size_t datalen, - int fin); - -int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, - const nghttp3_frame_settings *fr); - -int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, - nghttp3_stream *stream); - -void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn); - -int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id); - -int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id); - -int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn); - -int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); - -int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, - nghttp3_stream *stream); - -void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); - -/* - * nghttp3_conn_get_next_tx_stream returns next stream to send. It - * returns NULL if there is no such stream. - */ -nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn); - -int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, - uint64_t seq, nghttp3_tnode *assoc_tnode, - const nghttp3_mem *mem); - -void nghttp3_push_promise_del(nghttp3_push_promise *pp, const nghttp3_mem *mem); - -#endif /* NGHTTP3_CONN_H */ diff --git a/deps/nghttp3/lib/nghttp3_conv.c b/deps/nghttp3/lib/nghttp3_conv.c deleted file mode 100644 index 04bf6a0f298..00000000000 --- a/deps/nghttp3/lib/nghttp3_conv.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 "nghttp3_conv.h" - -#include <string.h> -#include <assert.h> - -#include "nghttp3_str.h" - -int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) { - union { - char b[8]; - uint16_t n16; - uint32_t n32; - uint64_t n64; - } n; - - *plen = 1u << (*p >> 6); - - switch (*plen) { - case 1: - return (int64_t)*p; - case 2: - memcpy(&n, p, 2); - n.b[0] &= 0x3f; - return (int64_t)ntohs(n.n16); - case 4: - memcpy(&n, p, 4); - n.b[0] &= 0x3f; - return (int64_t)ntohl(n.n32); - case 8: - memcpy(&n, p, 8); - n.b[0] &= 0x3f; - return (int64_t)nghttp3_ntohl64(n.n64); - } - - assert(0); -} - -int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } - -size_t nghttp3_get_varint_len(const uint8_t *p) { return 1u << (*p >> 6); } - -uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n) { - n = nghttp3_htonl64(n); - return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); -} - -uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n) { - n = nghttp3_htonl64(n); - return nghttp3_cpymem(p, ((const uint8_t *)&n) + 2, 6); -} - -uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n) { - n = htonl(n); - return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); -} - -uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n) { - n = htonl(n); - return nghttp3_cpymem(p, ((const uint8_t *)&n) + 1, 3); -} - -uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n) { - n = htons(n); - return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); -} - -uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n) { - uint8_t *rv; - if (n < 64) { - *p++ = (uint8_t)n; - return p; - } - if (n < 16384) { - rv = nghttp3_put_uint16be(p, (uint16_t)n); - *p |= 0x40; - return rv; - } - if (n < 1073741824) { - rv = nghttp3_put_uint32be(p, (uint32_t)n); - *p |= 0x80; - return rv; - } - assert(n < 4611686018427387904LL); - rv = nghttp3_put_uint64be(p, (uint64_t)n); - *p |= 0xc0; - return rv; -} - -size_t nghttp3_put_varint_len(int64_t n) { - if (n < 64) { - return 1; - } - if (n < 16384) { - return 2; - } - if (n < 1073741824) { - return 4; - } - assert(n < 4611686018427387904LL); - return 8; -} - -uint64_t nghttp3_ord_stream_id(int64_t stream_id) { - return (uint64_t)(stream_id >> 2) + 1; -} - -uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri) { - return (uint8_t)((uint32_t)pri->inc << 7 | pri->urgency); -} diff --git a/deps/nghttp3/lib/nghttp3_conv.h b/deps/nghttp3/lib/nghttp3_conv.h deleted file mode 100644 index 6fae9fc9684..00000000000 --- a/deps/nghttp3/lib/nghttp3_conv.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 NGHTTP3_CONV_H -#define NGHTTP3_CONV_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#ifdef HAVE_ARPA_INET_H -# include <arpa/inet.h> -#endif /* HAVE_ARPA_INET_H */ - -#ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -#endif /* HAVE_NETINET_IN_H */ - -#ifdef HAVE_BYTESWAP_H -# include <byteswap.h> -#endif /* HAVE_BYTESWAP_H */ - -#ifdef HAVE_ENDIAN_H -# include <endian.h> -#endif /* HAVE_ENDIAN_H */ - -#ifdef HAVE_SYS_ENDIAN_H -# include <sys/endian.h> -#endif /* HAVE_SYS_ENDIAN_H */ - -#include <nghttp3/nghttp3.h> - -#if defined HAVE_BSWAP_64 || HAVE_DECL_BSWAP_64 -# define nghttp3_bswap64 bswap_64 -#else /* !HAVE_BSWAP_64 */ -# define nghttp3_bswap64(N) \ - ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) -#endif /* !HAVE_BSWAP_64 */ - -#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH -# define nghttp3_ntohl64(N) be64toh(N) -# define nghttp3_htonl64(N) htobe64(N) -#else /* !HAVE_BE64TOH */ -# if defined WORDS_BIGENDIAN -# define nghttp3_ntohl64(N) (N) -# define nghttp3_htonl64(N) (N) -# else /* !WORDS_BIGENDIAN */ -# define nghttp3_ntohl64(N) nghttp3_bswap64(N) -# define nghttp3_htonl64(N) nghttp3_bswap64(N) -# endif /* !WORDS_BIGENDIAN */ -#endif /* !HAVE_BE64TOH */ - -#if defined(WIN32) -/* Windows requires ws2_32 library for ntonl family of functions. We - define inline functions for those functions so that we don't have - dependency on that lib. */ - -# ifdef _MSC_VER -# define STIN static __inline -# else -# define STIN static inline -# endif - -STIN uint32_t htonl(uint32_t hostlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; - *p++ = (hostlong >> 16) & 0xffu; - *p++ = (hostlong >> 8) & 0xffu; - *p = hostlong & 0xffu; - return res; -} - -STIN uint16_t htons(uint16_t hostshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; - *p = hostshort & 0xffu; - return res; -} - -STIN uint32_t ntohl(uint32_t netlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; - res += *p; - return res; -} - -STIN uint16_t ntohs(uint16_t netshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; - res += *p; - return res; -} - -#endif /* WIN32 */ - -/* - * nghttp3_get_varint reads variable-length integer from |p|, and - * returns it in host byte order. The number of bytes read is stored - * in |*plen|. - */ -int64_t nghttp3_get_varint(size_t *plen, 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_varint_len returns the required number of bytes to read - * variable-length integer starting at |p|. - */ -size_t nghttp3_get_varint_len(const uint8_t *p); - -/* - * nghttp3_put_uint64be writes |n| in host byte order in |p| in - * network byte order. It returns the one beyond of the last written - * position. - */ -uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n); - -/* - * nghttp3_put_uint48be writes |n| in host byte order in |p| in - * network byte order. It writes only least significant 48 bits. It - * returns the one beyond of the last written position. - */ -uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n); - -/* - * nghttp3_put_uint32be writes |n| in host byte order in |p| in - * network byte order. It returns the one beyond of the last written - * position. - */ -uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n); - -/* - * nghttp3_put_uint24be writes |n| in host byte order in |p| in - * network byte order. It writes only least significant 24 bits. It - * returns the one beyond of the last written position. - */ -uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n); - -/* - * nghttp3_put_uint16be writes |n| in host byte order in |p| in - * network byte order. It returns the one beyond of the last written - * position. - */ -uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n); - -/* - * nghttp3_put_varint writes |n| in |p| using variable-length integer - * encoding. It returns the one beyond of the last written position. - */ -uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n); - -/* - * nghttp3_put_varint_len returns the required number of bytes to - * encode |n|. - */ -size_t nghttp3_put_varint_len(int64_t n); - -/* - * nghttp3_ord_stream_id returns the ordinal number of |stream_id|. - */ -uint64_t nghttp3_ord_stream_id(int64_t stream_id); - -/* - * NGHTTP3_PRI_INC_MASK is a bit mask to retrieve incremental bit from - * a value produced by nghttp3_pri_to_uint8. - */ -#define NGHTTP3_PRI_INC_MASK (1 << 7) - -/* - * nghttp3_pri_to_uint8 encodes |pri| into uint8_t variable. - */ -uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri); - -/* - * nghttp3_pri_uint8_urgency extracts urgency from |PRI| which is - * supposed to be constructed by nghttp3_pri_to_uint8. - */ -#define nghttp3_pri_uint8_urgency(PRI) \ - ((uint32_t)((PRI) & ~NGHTTP3_PRI_INC_MASK)) - -/* - * nghttp3_pri_uint8_inc extracts inc from |PRI| which is supposed to - * be constructed by nghttp3_pri_to_uint8. - */ -#define nghttp3_pri_uint8_inc(PRI) (((PRI)&NGHTTP3_PRI_INC_MASK) != 0) - -#endif /* NGHTTP3_CONV_H */ diff --git a/deps/nghttp3/lib/nghttp3_debug.c b/deps/nghttp3/lib/nghttp3_debug.c deleted file mode 100644 index 4021b0dc469..00000000000 --- a/deps/nghttp3/lib/nghttp3_debug.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2016 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 "nghttp3_debug.h" - -#include <stdio.h> - -#ifdef DEBUGBUILD - -static void nghttp3_default_debug_vfprintf_callback(const char *fmt, - va_list args) { - vfprintf(stderr, fmt, args); -} - -static nghttp3_debug_vprintf_callback static_debug_vprintf_callback = - nghttp3_default_debug_vfprintf_callback; - -void nghttp3_debug_vprintf(const char *format, ...) { - if (static_debug_vprintf_callback) { - va_list args; - va_start(args, format); - static_debug_vprintf_callback(format, args); - va_end(args); - } -} - -void nghttp3_set_debug_vprintf_callback( - nghttp3_debug_vprintf_callback debug_vprintf_callback) { - static_debug_vprintf_callback = debug_vprintf_callback; -} - -#else /* !DEBUGBUILD */ - -void nghttp3_set_debug_vprintf_callback( - nghttp3_debug_vprintf_callback debug_vprintf_callback) { - (void)debug_vprintf_callback; -} - -#endif /* !DEBUGBUILD */ diff --git a/deps/nghttp3/lib/nghttp3_debug.h b/deps/nghttp3/lib/nghttp3_debug.h deleted file mode 100644 index 01ed918414c..00000000000 --- a/deps/nghttp3/lib/nghttp3_debug.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2016 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. - */ -#ifndef NGHTTP3_DEBUG_H -#define NGHTTP3_DEBUG_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#ifdef DEBUGBUILD -# define DEBUGF(...) nghttp3_debug_vprintf(__VA_ARGS__) -void nghttp3_debug_vprintf(const char *format, ...); -#else -# define DEBUGF(...) \ - do { \ - } while (0) -#endif - -#endif /* NGHTTP3_DEBUG_H */ diff --git a/deps/nghttp3/lib/nghttp3_err.c b/deps/nghttp3/lib/nghttp3_err.c deleted file mode 100644 index 23033d4640d..00000000000 --- a/deps/nghttp3/lib/nghttp3_err.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_err.h" - -const char *nghttp3_strerror(int liberr) { - switch (liberr) { - case NGHTTP3_ERR_INVALID_ARGUMENT: - return "ERR_INVALID_ARGUMENT"; - case NGHTTP3_ERR_NOBUF: - return "ERR_NOBUF"; - case NGHTTP3_ERR_INVALID_STATE: - return "ERR_INVALID_STATE"; - case NGHTTP3_ERR_WOULDBLOCK: - return "ERR_WOULDBLOCK"; - case NGHTTP3_ERR_STREAM_IN_USE: - return "ERR_STREAM_IN_USE"; - case NGHTTP3_ERR_PUSH_ID_BLOCKED: - return "ERR_PUSH_ID_BLOCKED"; - case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: - return "ERR_MALFORMED_HTTP_HEADER"; - case NGHTTP3_ERR_REMOVE_HTTP_HEADER: - return "ERR_REMOVE_HTTP_HEADER"; - case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING: - return "ERR_MALFORMED_HTTP_MESSAGING"; - case NGHTTP3_ERR_TOO_LATE: - return "ERR_TOO_LATE"; - case NGHTTP3_ERR_QPACK_FATAL: - return "ERR_QPACK_FATAL"; - case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE: - return "ERR_QPACK_HEADER_TOO_LARGE"; - case NGHTTP3_ERR_IGNORE_STREAM: - return "ERR_IGNORE_STREAM"; - case NGHTTP3_ERR_STREAM_NOT_FOUND: - return "ERR_STREAM_NOT_FOUND"; - case NGHTTP3_ERR_IGNORE_PUSH_PROMISE: - return "ERR_IGNORE_PUSH_PROMISE"; - case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: - return "ERR_QPACK_DECOMPRESSION_FAILED"; - case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: - return "ERR_QPACK_ENCODER_STREAM_ERROR"; - case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR: - return "ERR_QPACK_DECODER_STREAM_ERROR"; - case NGHTTP3_ERR_H3_FRAME_UNEXPECTED: - return "ERR_H3_FRAME_UNEXPECTED"; - case NGHTTP3_ERR_H3_FRAME_ERROR: - return "ERR_H3_FRAME_ERROR"; - case NGHTTP3_ERR_H3_MISSING_SETTINGS: - return "ERR_H3_MISSING_SETTINGS"; - case NGHTTP3_ERR_H3_INTERNAL_ERROR: - return "ERR_H3_INTERNAL_ERROR"; - case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: - return "ERR_CLOSED_CRITICAL_STREAM"; - case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR: - return "ERR_H3_GENERAL_PROTOCOL_ERROR"; - case NGHTTP3_ERR_H3_ID_ERROR: - return "ERR_H3_ID_ERROR"; - case NGHTTP3_ERR_H3_SETTINGS_ERROR: - return "ERR_H3_SETTINGS_ERROR"; - case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR: - return "ERR_H3_STREAM_CREATION_ERROR"; - case NGHTTP3_ERR_NOMEM: - return "ERR_NOMEM"; - case NGHTTP3_ERR_CALLBACK_FAILURE: - return "ERR_CALLBACK_FAILURE"; - default: - return "(unknown)"; - } -} - -uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) { - switch (liberr) { - case 0: - return NGHTTP3_H3_NO_ERROR; - case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: - return NGHTTP3_QPACK_DECOMPRESSION_FAILED; - case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: - return NGHTTP3_QPACK_ENCODER_STREAM_ERROR; - case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR: - return NGHTTP3_QPACK_DECODER_STREAM_ERROR; - case NGHTTP3_ERR_H3_FRAME_UNEXPECTED: - return NGHTTP3_H3_FRAME_UNEXPECTED; - case NGHTTP3_ERR_H3_FRAME_ERROR: - return NGHTTP3_H3_FRAME_ERROR; - case NGHTTP3_ERR_H3_MISSING_SETTINGS: - return NGHTTP3_H3_MISSING_SETTINGS; - case NGHTTP3_ERR_H3_INTERNAL_ERROR: - return NGHTTP3_H3_INTERNAL_ERROR; - case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: - return NGHTTP3_H3_CLOSED_CRITICAL_STREAM; - case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR: - return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; - case NGHTTP3_ERR_H3_ID_ERROR: - return NGHTTP3_H3_ID_ERROR; - case NGHTTP3_ERR_H3_SETTINGS_ERROR: - return NGHTTP3_H3_SETTINGS_ERROR; - case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR: - return NGHTTP3_H3_STREAM_CREATION_ERROR; - default: - return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; - } -} diff --git a/deps/nghttp3/lib/nghttp3_err.h b/deps/nghttp3/lib/nghttp3_err.h deleted file mode 100644 index 2fa914f86b1..00000000000 --- a/deps/nghttp3/lib/nghttp3_err.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_ERR_H -#define NGHTTP3_ERR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#endif /* NGHTTP3_ERR_H */ diff --git a/deps/nghttp3/lib/nghttp3_frame.c b/deps/nghttp3/lib/nghttp3_frame.c deleted file mode 100644 index 479b794f9ae..00000000000 --- a/deps/nghttp3/lib/nghttp3_frame.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2013 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 "nghttp3_frame.h" - -#include <string.h> - -#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); - return p; -} - -size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) { - return nghttp3_put_varint_len(hd->type) + nghttp3_put_varint_len(hd->length); -} - -uint8_t *nghttp3_frame_write_settings(uint8_t *p, - const nghttp3_frame_settings *fr) { - size_t i; - - p = nghttp3_frame_write_hd(p, &fr->hd); - - for (i = 0; i < fr->niv; ++i) { - p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id); - p = nghttp3_put_varint(p, (int64_t)fr->iv[i].value); - } - - return p; -} - -size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen, - const nghttp3_frame_settings *fr) { - size_t payloadlen = 0; - size_t i; - - for (i = 0; i < fr->niv; ++i) { - payloadlen += nghttp3_put_varint_len((int64_t)fr->iv[i].id) + - nghttp3_put_varint_len((int64_t)fr->iv[i].value); - } - - *ppayloadlen = (int64_t)payloadlen; - - return nghttp3_put_varint_len(NGHTTP3_FRAME_SETTINGS) + - nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; -} - -uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p, - const nghttp3_frame_cancel_push *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->push_id); - - return p; -} - -size_t -nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, - const nghttp3_frame_cancel_push *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->push_id); - - *ppayloadlen = (int64_t)payloadlen; - - return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) + - nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; -} - -uint8_t *nghttp3_frame_write_max_push_id(uint8_t *p, - const nghttp3_frame_max_push_id *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->push_id); - - return p; -} - -size_t -nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, - const nghttp3_frame_max_push_id *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->push_id); - - *ppayloadlen = (int64_t)payloadlen; - - return nghttp3_put_varint_len(NGHTTP3_FRAME_MAX_PUSH_ID) + - nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; -} - -int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen, - const nghttp3_mem *mem) { - size_t i; - uint8_t *data = NULL; - size_t buflen = 0; - nghttp3_nv *p; - - if (nvlen == 0) { - *pnva = NULL; - - return 0; - } - - for (i = 0; i < nvlen; ++i) { - /* + 1 for null-termination */ - if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) == 0) { - buflen += nva[i].namelen + 1; - } - if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) == 0) { - buflen += nva[i].valuelen + 1; - } - } - - buflen += sizeof(nghttp3_nv) * nvlen; - - *pnva = nghttp3_mem_malloc(mem, buflen); - - if (*pnva == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - p = *pnva; - data = (uint8_t *)(*pnva) + sizeof(nghttp3_nv) * nvlen; - - for (i = 0; i < nvlen; ++i) { - p->flags = nva[i].flags; - - if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) { - p->name = nva[i].name; - p->namelen = nva[i].namelen; - } else { - if (nva[i].namelen) { - memcpy(data, nva[i].name, nva[i].namelen); - } - p->name = data; - p->namelen = nva[i].namelen; - data[p->namelen] = '\0'; - nghttp3_downcase(p->name, p->namelen); - data += nva[i].namelen + 1; - } - - if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) { - p->value = nva[i].value; - p->valuelen = nva[i].valuelen; - } else { - if (nva[i].valuelen) { - memcpy(data, nva[i].value, nva[i].valuelen); - } - p->value = data; - p->valuelen = nva[i].valuelen; - data[p->valuelen] = '\0'; - data += nva[i].valuelen + 1; - } - - ++p; - } - return 0; -} - -void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem) { - nghttp3_mem_free(mem, nva); -} - -void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, - const nghttp3_mem *mem) { - if (fr == NULL) { - return; - } - - nghttp3_nva_del(fr->nva, mem); -} - -void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, - const nghttp3_mem *mem) { - if (fr == NULL) { - return; - } - - nghttp3_nva_del(fr->nva, mem); -} diff --git a/deps/nghttp3/lib/nghttp3_frame.h b/deps/nghttp3/lib/nghttp3_frame.h deleted file mode 100644 index be333e95090..00000000000 --- a/deps/nghttp3/lib/nghttp3_frame.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2013 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. - */ -#ifndef NGHTTP3_FRAME_H -#define NGHTTP3_FRAME_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_buf.h" - -typedef enum { - NGHTTP3_FRAME_DATA = 0x00, - NGHTTP3_FRAME_HEADERS = 0x01, - NGHTTP3_FRAME_CANCEL_PUSH = 0x03, - NGHTTP3_FRAME_SETTINGS = 0x04, - NGHTTP3_FRAME_PUSH_PROMISE = 0x05, - NGHTTP3_FRAME_GOAWAY = 0x07, - NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d, -} nghttp3_frame_type; - -typedef struct { - int64_t type; - int64_t length; -} nghttp3_frame_hd; - -typedef struct { - nghttp3_frame_hd hd; -} nghttp3_frame_data; - -typedef struct { - nghttp3_frame_hd hd; - nghttp3_nv *nva; - size_t nvlen; -} nghttp3_frame_headers; - -typedef struct { - nghttp3_frame_hd hd; - int64_t push_id; -} nghttp3_frame_cancel_push; - -#define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06 -#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01 -#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07 - -typedef struct { - uint64_t id; - uint64_t value; -} nghttp3_settings_entry; - -typedef struct { - nghttp3_frame_hd hd; - size_t niv; - nghttp3_settings_entry iv[1]; -} nghttp3_frame_settings; - -typedef struct { - nghttp3_frame_hd hd; - nghttp3_nv *nva; - size_t nvlen; - int64_t push_id; -} nghttp3_frame_push_promise; - -typedef struct { - nghttp3_frame_hd hd; - int64_t stream_id; -} nghttp3_frame_goaway; - -typedef struct { - nghttp3_frame_hd hd; - int64_t push_id; -} nghttp3_frame_max_push_id; - -typedef union { - nghttp3_frame_hd hd; - nghttp3_frame_data data; - nghttp3_frame_headers headers; - nghttp3_frame_cancel_push cancel_push; - nghttp3_frame_settings settings; - nghttp3_frame_push_promise push_promise; - nghttp3_frame_goaway goaway; - nghttp3_frame_max_push_id max_push_id; -} nghttp3_frame; - -/* - * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This - * function assumes that |dest| has enough space to write |hd|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd); - -/* - * nghttp3_frame_write_hd_len returns the number of bytes required to - * write |hd|. hd->length must be set. - */ -size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd); - -/* - * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|. - * This function assumes that |dest| has enough space to write |fr|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_settings(uint8_t *dest, - const nghttp3_frame_settings *fr); - -/* - * 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|. - */ -size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, - const nghttp3_frame_settings *fr); - -/* - * nghttp3_frame_write_cancel_push writes CANCEL_PUSH frame |fr| to - * |dest|. This function assumes that |dest| has enough space to - * write |fr|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_cancel_push(uint8_t *dest, - const nghttp3_frame_cancel_push *fr); - -/* - * nghttp3_frame_write_cancel_push_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. - */ -size_t nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, - const nghttp3_frame_cancel_push *fr); - -/* - * nghttp3_frame_write_max_push_id writes MAX_PUSH_ID frame |fr| to - * |dest|. This function assumes that |dest| has enough space to - * write |fr|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_max_push_id(uint8_t *dest, - const nghttp3_frame_max_push_id *fr); - -/* - * nghttp3_frame_write_max_push_id_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. - */ -size_t nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, - const nghttp3_frame_max_push_id *fr); - -/* - * nghttp3_nva_copy copies name/value pairs from |nva|, which contains - * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so - * that all items can be stored. The resultant name and value in - * nghttp2_nv are guaranteed to be NULL-terminated even if the input - * is not null-terminated. - * - * The |*pnva| must be freed using nghttp3_nva_del(). - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen, - const nghttp3_mem *mem); - -/* - * nghttp3_nva_del frees |nva|. - */ -void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem); - -/* - * nghttp3_frame_headers_free frees memory allocated for |fr|. It - * assumes that fr->nva is created by nghttp3_nva_copy() or NULL. - */ -void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, - const nghttp3_mem *mem); - -/* - * nghttp3_frame_push_promise_free frees memory allocated for |fr|. - * It assumes that fr->nva is created by nghttp3_nva_copy() or NULL. - */ -void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, - const nghttp3_mem *mem); - -#endif /* NGHTTP3_FRAME_H */ diff --git a/deps/nghttp3/lib/nghttp3_gaptr.c b/deps/nghttp3/lib/nghttp3_gaptr.c deleted file mode 100644 index 1faabbf09f8..00000000000 --- a/deps/nghttp3/lib/nghttp3_gaptr.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 "nghttp3_gaptr.h" - -#include <string.h> -#include <assert.h> - -#include "nghttp3_macro.h" - -int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { - int rv; - nghttp3_range range = {0, UINT64_MAX}; - - rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, - sizeof(nghttp3_range), mem); - if (rv != 0) { - return rv; - } - - rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL); - if (rv != 0) { - nghttp3_ksl_free(&gaptr->gap); - return rv; - } - - gaptr->mem = mem; - - return 0; -} - -void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) { - if (gaptr == NULL) { - return; - } - - nghttp3_ksl_free(&gaptr->gap); -} - -int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { - int rv; - nghttp3_range k, m, l, r, q = {offset, offset + datalen}; - nghttp3_ksl_it it; - - it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); - - for (; !nghttp3_ksl_it_end(&it);) { - k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); - m = nghttp3_range_intersect(&q, &k); - if (!nghttp3_range_len(&m)) { - break; - } - - if (nghttp3_range_eq(&k, &m)) { - nghttp3_ksl_remove(&gaptr->gap, &it, &k); - continue; - } - nghttp3_range_cut(&l, &r, &k, &m); - if (nghttp3_range_len(&l)) { - nghttp3_ksl_update_key(&gaptr->gap, &k, &l); - - if (nghttp3_range_len(&r)) { - rv = nghttp3_ksl_insert(&gaptr->gap, &it, &r, NULL); - if (rv != 0) { - return rv; - } - } - } else if (nghttp3_range_len(&r)) { - nghttp3_ksl_update_key(&gaptr->gap, &k, &r); - } - nghttp3_ksl_it_next(&it); - } - return 0; -} - -uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { - nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); - return ((nghttp3_range *)nghttp3_ksl_it_key(&it))->begin; -} - -nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, - uint64_t offset) { - nghttp3_range q = {offset, offset + 1}; - return nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); -} - -int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, - size_t datalen) { - nghttp3_range q = {offset, offset + datalen}; - nghttp3_ksl_it it = nghttp3_ksl_lower_bound_compar( - &gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar); - nghttp3_range m = - nghttp3_range_intersect(&q, (nghttp3_range *)nghttp3_ksl_it_key(&it)); - return nghttp3_range_len(&m) == 0; -} diff --git a/deps/nghttp3/lib/nghttp3_gaptr.h b/deps/nghttp3/lib/nghttp3_gaptr.h deleted file mode 100644 index 6972b404432..00000000000 --- a/deps/nghttp3/lib/nghttp3_gaptr.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 NGHTTP3_GAPTR_H -#define NGHTTP3_GAPTR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_mem.h" -#include "nghttp3_range.h" -#include "nghttp3_ksl.h" - -/* - * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX). - */ -typedef struct { - /* gap maintains the range of offset which is not received - yet. Initially, its range is [0, UINT64_MAX). */ - nghttp3_ksl gap; - /* mem is custom memory allocator */ - const nghttp3_mem *mem; -} nghttp3_gaptr; - -/* - * nghttp3_gaptr_init initializes |gaptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem); - -/* - * nghttp3_gaptr_free frees resources allocated for |gaptr|. - */ -void nghttp3_gaptr_free(nghttp3_gaptr *gaptr); - -/* - * nghttp3_gaptr_push adds new data of length |datalen| at the stream - * offset |offset|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory - */ -int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen); - -/* - * nghttp3_gaptr_first_gap_offset returns the offset to the first gap. - * If there is no gap, it returns UINT64_MAX. - */ -uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr); - -/* - * nghttp3_gaptr_get_first_gap_after returns the iterator pointing to - * the first gap which overlaps or comes after |offset|. - */ -nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, - uint64_t offset); - -/* - * nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset + - * datalen) is completely pushed into this object. - */ -int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, - size_t datalen); - -#endif /* NGHTTP3_GAPTR_H */ diff --git a/deps/nghttp3/lib/nghttp3_http.c b/deps/nghttp3/lib/nghttp3_http.c deleted file mode 100644 index dc84c16b5d8..00000000000 --- a/deps/nghttp3/lib/nghttp3_http.c +++ /dev/null @@ -1,840 +0,0 @@ -/* - * nghttp3 - * - * 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 "nghttp3_http.h" - -#include <string.h> -#include <assert.h> - -#include "nghttp3_stream.h" -#include "nghttp3_macro.h" -#include "nghttp3_conv.h" - -static uint8_t downcase(uint8_t c) { - return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; -} - -static int memieq(const void *a, const void *b, size_t n) { - size_t i; - const uint8_t *aa = a, *bb = b; - - for (i = 0; i < n; ++i) { - if (downcase(aa[i]) != downcase(bb[i])) { - return 0; - } - } - return 1; -} - -#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) - -static int64_t parse_uint(const uint8_t *s, size_t len) { - int64_t n = 0; - size_t i; - if (len == 0) { - return -1; - } - for (i = 0; i < len; ++i) { - if ('0' <= s[i] && s[i] <= '9') { - if (n > INT64_MAX / 10) { - return -1; - } - n *= 10; - if (n > INT64_MAX - (s[i] - '0')) { - return -1; - } - n += s[i] - '0'; - continue; - } - return -1; - } - return n; -} - -static int lws(const uint8_t *s, size_t n) { - size_t i; - for (i = 0; i < n; ++i) { - if (s[i] != ' ' && s[i] != '\t') { - return 0; - } - } - return 1; -} - -static int check_pseudo_header(nghttp3_http_state *http, - const nghttp3_qpack_nv *nv, int flag) { - if (http->flags & flag) { - return 0; - } - if (lws(nv->value->base, nv->value->len)) { - return 0; - } - http->flags = (uint16_t)(http->flags | flag); - return 1; -} - -static int expect_response_body(nghttp3_http_state *http) { - return (http->flags & NGHTTP3_HTTP_FLAG_METH_HEAD) == 0 && - http->status_code / 100 != 1 && http->status_code != 304 && - http->status_code != 204; -} - -/* For "http" or "https" URIs, OPTIONS request may have "*" in :path - header field to represent system-wide OPTIONS request. Otherwise, - :path header field value must start with "/". This function must - be called after ":method" header field was received. This function - returns nonzero if path is valid.*/ -static int check_path(nghttp3_http_state *http) { - return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 || - ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) || - ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) && - (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK))); -} - -int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, - size_t len) { - nghttp3_pri pri = *dest; - const uint8_t *p = value, *end = value + len; - - for (;;) { - for (; p != end && (*p == ' ' || *p == '\t'); ++p) - ; - - if (p == end) { - break; - } - - switch (*p) { - case 'u': - ++p; - - if (p + 2 > end || *p++ != '=') { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (!('0' <= *p && *p <= '7')) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - pri.urgency = (uint32_t)(*p++ - '0'); - - if (p == end) { - goto fin; - } - - if (*p++ != ',') { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - break; - case 'i': - ++p; - - if (p == end) { - pri.inc = 1; - goto fin; - } - - if (*p == ',') { - pri.inc = 1; - ++p; - break; - } - - if (p + 3 > end || *p != '=' || *(p + 1) != '?' || - (*(p + 2) != '0' && *(p + 2) != '1')) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - pri.inc = *(p + 2) == '1'; - - p += 3; - - if (p == end) { - goto fin; - } - - if (*p++ != ',') { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - break; - default: - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (p == end) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - } - -fin: - - *dest = pri; - - return 0; -} - -static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, - nghttp3_qpack_nv *nv, int trailers, - int connect_protocol) { - nghttp3_pri pri; - - if (nv->name->base[0] == ':') { - if (trailers || - (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - } - - switch (nv->token) { - case NGHTTP3_QPACK_TOKEN__AUTHORITY: - if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - case NGHTTP3_QPACK_TOKEN__METHOD: - if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__METHOD)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - switch (nv->value->len) { - case 4: - if (lstreq("HEAD", nv->value->base, nv->value->len)) { - http->flags |= NGHTTP3_HTTP_FLAG_METH_HEAD; - } - break; - case 7: - switch (nv->value->base[6]) { - case 'T': - if (lstreq("CONNECT", nv->value->base, nv->value->len)) { - if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { - /* we won't allow CONNECT for push */ - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; - } - break; - case 'S': - if (lstreq("OPTIONS", nv->value->base, nv->value->len)) { - http->flags |= NGHTTP3_HTTP_FLAG_METH_OPTIONS; - } - break; - } - break; - } - break; - case NGHTTP3_QPACK_TOKEN__PATH: - if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PATH)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - if (nv->value->base[0] == '/') { - http->flags |= NGHTTP3_HTTP_FLAG_PATH_REGULAR; - } else if (nv->value->len == 1 && nv->value->base[0] == '*') { - http->flags |= NGHTTP3_HTTP_FLAG_PATH_ASTERISK; - } - break; - case NGHTTP3_QPACK_TOKEN__SCHEME: - if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || - (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { - http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP; - } - break; - case NGHTTP3_QPACK_TOKEN__PROTOCOL: - if (!connect_protocol) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - - if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - case NGHTTP3_QPACK_TOKEN_HOST: - if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { - /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender - MUST NOT generate a trailer that contains a field necessary for - message framing (e.g., Transfer-Encoding and Content-Length), - ... */ - if (trailers) { - return NGHTTP3_ERR_REMOVE_HTTP_HEADER; - } - if (http->content_length != -1) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - http->content_length = parse_uint(nv->value->base, nv->value->len); - if (http->content_length == -1) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - } - /* disallowed header fields */ - case NGHTTP3_QPACK_TOKEN_CONNECTION: - case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE: - case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION: - case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING: - case NGHTTP3_QPACK_TOKEN_UPGRADE: - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - case NGHTTP3_QPACK_TOKEN_TE: - if (!lstrieq("trailers", nv->value->base, nv->value->len)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - case NGHTTP3_QPACK_TOKEN_PRIORITY: - pri.urgency = nghttp3_pri_uint8_urgency(http->pri); - pri.inc = nghttp3_pri_uint8_inc(http->pri); - if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) == - 0) { - http->pri = nghttp3_pri_to_uint8(&pri); - } - break; - default: - if (nv->name->base[0] == ':') { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - } - - if (nv->name->base[0] != ':') { - http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - } - - return 0; -} - -static int http_response_on_header(nghttp3_http_state *http, - nghttp3_qpack_nv *nv, int trailers) { - if (nv->name->base[0] == ':') { - if (trailers || - (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - } - - switch (nv->token) { - case NGHTTP3_QPACK_TOKEN__STATUS: { - if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - if (nv->value->len != 3) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); - if (http->status_code < 100 || http->status_code == 101) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - } - case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { - /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender - MUST NOT generate a trailer that contains a field necessary for - message framing (e.g., Transfer-Encoding and Content-Length), - ... */ - if (trailers) { - return NGHTTP3_ERR_REMOVE_HTTP_HEADER; - } - if (http->status_code == 204) { - /* content-length header field in 204 response is prohibited by - RFC 7230. But some widely used servers send content-length: - 0. Until they get fixed, we ignore it. */ - if (http->content_length != -1) { - /* Found multiple content-length field */ - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - if (!lstrieq("0", nv->value->base, nv->value->len)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - http->content_length = 0; - return NGHTTP3_ERR_REMOVE_HTTP_HEADER; - } - if (http->status_code / 100 == 1) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */ - if (http->status_code / 100 == 2 && - (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) { - return NGHTTP3_ERR_REMOVE_HTTP_HEADER; - } - if (http->content_length != -1) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - http->content_length = parse_uint(nv->value->base, nv->value->len); - if (http->content_length == -1) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - } - /* disallowed header fields */ - case NGHTTP3_QPACK_TOKEN_CONNECTION: - case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE: - case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION: - case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING: - case NGHTTP3_QPACK_TOKEN_UPGRADE: - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - case NGHTTP3_QPACK_TOKEN_TE: - if (!lstrieq("trailers", nv->value->base, nv->value->len)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - break; - default: - if (nv->name->base[0] == ':') { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - } - - if (nv->name->base[0] != ':') { - http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - } - - return 0; -} - -/* Generated by genauthroitychartbl.py */ -static char VALID_AUTHORITY_CHARS[] = { - 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, - 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, - 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, - 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, - 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, - 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, - 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, - 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, - 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */, - 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, - 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, - 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, - 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, - 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 */, - 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, - 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, - 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, - 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, - 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, - 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, - 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */, - 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 */, - 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, - 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, - 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, - 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, - 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, - 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, - 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, - 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, - 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, - 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, - 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, - 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, - 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, - 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, - 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, - 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, - 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, - 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, - 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, - 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, - 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, - 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, - 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, - 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, - 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, - 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, - 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, - 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, - 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, - 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, - 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, - 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, - 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, - 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, - 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, - 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ -}; - -static int check_authority(const uint8_t *value, size_t len) { - const uint8_t *last; - for (last = value + len; value != last; ++value) { - if (!VALID_AUTHORITY_CHARS[*value]) { - return 0; - } - } - return 1; -} - -static int check_scheme(const uint8_t *value, size_t len) { - const uint8_t *last; - if (len == 0) { - return 0; - } - - if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) { - return 0; - } - - last = value + len; - ++value; - - for (; value != last; ++value) { - if (!(('A' <= *value && *value <= 'Z') || - ('a' <= *value && *value <= 'z') || - ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' || - *value == '.')) { - return 0; - } - } - return 1; -} - -int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, - nghttp3_qpack_nv *nv, int request, int trailers) { - int rv; - size_t i; - uint8_t c; - - if (!nghttp3_check_header_name(nv->name->base, nv->name->len)) { - if (nv->name->len > 0 && nv->name->base[0] == ':') { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - /* header field name must be lower-cased without exception */ - for (i = 0; i < nv->name->len; ++i) { - c = nv->name->base[i]; - if ('A' <= c && c <= 'Z') { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - } - /* When ignoring regular header fields, we set this flag so that - we still enforce header field ordering rule for pseudo header - fields. */ - http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - return NGHTTP3_ERR_REMOVE_HTTP_HEADER; - } - - if (nv->token == NGHTTP3_QPACK_TOKEN__AUTHORITY || - nv->token == NGHTTP3_QPACK_TOKEN_HOST) { - rv = check_authority(nv->value->base, nv->value->len); - } else if (nv->token == NGHTTP3_QPACK_TOKEN__SCHEME) { - rv = check_scheme(nv->value->base, nv->value->len); - } else { - rv = nghttp3_check_header_value(nv->value->base, nv->value->len); - } - - if (rv == 0) { - assert(nv->name->len > 0); - if (nv->name->base[0] == ':') { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - /* When ignoring regular header fields, we set this flag so that - we still enforce header field ordering rule for pseudo header - fields. */ - http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - return NGHTTP3_ERR_REMOVE_HTTP_HEADER; - } - - if (request) { - return http_request_on_header(http, frame_type, nv, trailers, - /* connect_protocol = */ 0); - } - - return http_response_on_header(http, nv, trailers); -} - -int nghttp3_http_on_request_headers(nghttp3_http_state *http) { - if (!(http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) && - (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) { - if ((http->flags & (NGHTTP3_HTTP_FLAG__SCHEME | NGHTTP3_HTTP_FLAG__PATH)) || - (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - http->content_length = -1; - } else { - if ((http->flags & NGHTTP3_HTTP_FLAG_REQ_HEADERS) != - NGHTTP3_HTTP_FLAG_REQ_HEADERS || - (http->flags & - (NGHTTP3_HTTP_FLAG__AUTHORITY | NGHTTP3_HTTP_FLAG_HOST)) == 0) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - if ((http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) && - ((http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) == 0 || - (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - if (!check_path(http)) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - } - - return 0; -} - -int nghttp3_http_on_response_headers(nghttp3_http_state *http) { - if ((http->flags & NGHTTP3_HTTP_FLAG__STATUS) == 0) { - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } - - if (http->status_code / 100 == 1) { - /* non-final response */ - http->flags = (uint16_t)((http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) | - NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); - http->content_length = -1; - http->status_code = -1; - return 0; - } - - http->flags = - (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); - - if (!expect_response_body(http)) { - http->content_length = 0; - } else if (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { - http->content_length = -1; - } - - return 0; -} - -int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) { - if (stream->flags & NGHTTP3_STREAM_FLAG_RESET) { - return 0; - } - if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || - (stream->rx.http.content_length != -1 && - stream->rx.http.content_length != stream->rx.http.recv_content_length)) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } - - return 0; -} - -int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n) { - stream->rx.http.recv_content_length += (int64_t)n; - - if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || - (stream->rx.http.content_length != -1 && - stream->rx.http.recv_content_length > stream->rx.http.content_length)) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } - - return 0; -} - -void nghttp3_http_record_request_method(nghttp3_stream *stream, - const nghttp3_nv *nva, size_t nvlen) { - size_t i; - const nghttp3_nv *nv; - - /* TODO we should do this strictly. */ - for (i = 0; i < nvlen; ++i) { - nv = &nva[i]; - if (!(nv->namelen == 7 && nv->name[6] == 'd' && - memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { - continue; - } - if (lstreq("CONNECT", nv->value, nv->valuelen)) { - stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; - return; - } - if (lstreq("HEAD", nv->value, nv->valuelen)) { - stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_HEAD; - return; - } - return; - } -} - -/* Generated by gennmchartbl.py */ -static const int VALID_HD_NAME_CHARS[] = { - 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, - 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, - 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, - 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, - 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, - 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, - 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, - 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, - 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, - 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, - 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, - 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, - 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, - 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, - 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, - 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, - 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */, - 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */, - 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */, - 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, - 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, - 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */, - 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */, - 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, - 1 /* ` */, 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 */, - 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, - 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, - 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, - 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, - 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, - 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, - 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, - 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, - 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, - 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, - 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, - 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, - 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, - 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, - 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, - 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, - 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, - 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, - 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, - 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, - 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, - 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, - 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, - 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, - 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, - 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, - 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, - 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, - 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, - 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, - 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, - 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, - 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, - 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, - 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, - 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ -}; - -int nghttp3_check_header_name(const uint8_t *name, size_t len) { - const uint8_t *last; - if (len == 0) { - return 0; - } - if (*name == ':') { - if (len == 1) { - return 0; - } - ++name; - --len; - } - for (last = name + len; name != last; ++name) { - if (!VALID_HD_NAME_CHARS[*name]) { - return 0; - } - } - return 1; -} - -/* Generated by genvchartbl.py */ -static const int VALID_HD_VALUE_CHARS[] = { - 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, - 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, - 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */, - 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, - 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, - 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, - 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, - 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, - 1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, - 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, - 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, - 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, - 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, - 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, - 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, - 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, - 1 /* @ */, 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 */, - 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, - 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, - 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, - 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, - 1 /* ` */, 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 */, - 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, - 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, - 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, - 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, - 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, - 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, - 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, - 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, - 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, - 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, - 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, - 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, - 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, - 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, - 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, - 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, - 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, - 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, - 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, - 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, - 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, - 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, - 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, - 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, - 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, - 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, - 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, - 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, - 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, - 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, - 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, - 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, - 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, - 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, - 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, - 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ -}; - -int nghttp3_check_header_value(const uint8_t *value, size_t len) { - const uint8_t *last; - for (last = value + len; value != last; ++value) { - if (!VALID_HD_VALUE_CHARS[*value]) { - return 0; - } - } - return 1; -} diff --git a/deps/nghttp3/lib/nghttp3_http.h b/deps/nghttp3/lib/nghttp3_http.h deleted file mode 100644 index 00b74f2a260..00000000000 --- a/deps/nghttp3/lib/nghttp3_http.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * nghttp3 - * - * 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. - */ -#ifndef NGHTTP3_HTTP_H -#define NGHTTP3_HTTP_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -struct nghttp3_stream; -typedef struct nghttp3_stream nghttp3_stream; - -struct nghttp3_http_state; -typedef struct nghttp3_http_state nghttp3_http_state; - -/* HTTP related flags to enforce HTTP semantics */ -typedef enum { - NGHTTP3_HTTP_FLAG_NONE = 0, - /* header field seen so far */ - NGHTTP3_HTTP_FLAG__AUTHORITY = 1, - NGHTTP3_HTTP_FLAG__PATH = 1 << 1, - NGHTTP3_HTTP_FLAG__METHOD = 1 << 2, - NGHTTP3_HTTP_FLAG__SCHEME = 1 << 3, - /* host is not pseudo header, but we require either host or - :authority */ - NGHTTP3_HTTP_FLAG_HOST = 1 << 4, - NGHTTP3_HTTP_FLAG__STATUS = 1 << 5, - /* required header fields for HTTP request except for CONNECT - method. */ - NGHTTP3_HTTP_FLAG_REQ_HEADERS = NGHTTP3_HTTP_FLAG__METHOD | - NGHTTP3_HTTP_FLAG__PATH | - NGHTTP3_HTTP_FLAG__SCHEME, - NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, - /* HTTP method flags */ - NGHTTP3_HTTP_FLAG_METH_CONNECT = 1 << 7, - NGHTTP3_HTTP_FLAG_METH_HEAD = 1 << 8, - NGHTTP3_HTTP_FLAG_METH_OPTIONS = 1 << 9, - NGHTTP3_HTTP_FLAG_METH_ALL = NGHTTP3_HTTP_FLAG_METH_CONNECT | - NGHTTP3_HTTP_FLAG_METH_HEAD | - NGHTTP3_HTTP_FLAG_METH_OPTIONS, - /* :path category */ - /* path starts with "/" */ - NGHTTP3_HTTP_FLAG_PATH_REGULAR = 1 << 11, - /* path "*" */ - NGHTTP3_HTTP_FLAG_PATH_ASTERISK = 1 << 12, - /* scheme */ - /* "http" or "https" scheme */ - NGHTTP3_HTTP_FLAG_SCHEME_HTTP = 1 << 13, - /* set if final response is expected */ - NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, - NGHTTP3_HTTP_FLAG__PROTOCOL = 1 << 15, -} nghttp3_http_flag; - -/* - * This function is called when HTTP header field |nv| in a frame of type - * |frame_type| is received for |http|. This function will validate |nv| - * against the current state of stream. Pass nonzero if this is request - * headers. Pass nonzero to |trailers| if |nv| is included in trailers. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_MALFORMED_HTTP_HEADER - * Invalid HTTP header field was received. - * NGHTTP3_ERR_REMOVE_HTTP_HEADER - * Invalid HTTP header field was received but it can be treated as - * if it was not received because of compatibility reasons. - */ -int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, - nghttp3_qpack_nv *nv, int request, int trailers); - -/* - * This function is called when request header is received. This - * function performs validation and returns 0 if it succeeds, or one - * of the following negative error codes: - * - * NGHTTP3_ERR_MALFORMED_HTTP_HEADER - * Required HTTP header field was not received; or an invalid - * header field was received. - */ -int nghttp3_http_on_request_headers(nghttp3_http_state *http); - -/* - * This function is called when response header is received. This - * function performs validation and returns 0 if it succeeds, or one - * of the following negative error codes: - * - * NGHTTP3_ERR_MALFORMED_HTTP_HEADER - * Required HTTP header field was not received; or an invalid - * header field was received. - */ -int nghttp3_http_on_response_headers(nghttp3_http_state *http); - -/* - * This function is called when read side stream is closed. This - * function performs validation and returns 0 if it succeeds, or one - * of the following negative error codes: - * - * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING - * HTTP messaging is violated. - */ -int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream); - -/* - * This function is called when chunk of data is received. This - * function performs validation and returns 0 if it succeeds, or one - * of the following negative error codes: - * - * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING - * HTTP messaging is violated. - */ -int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n); - -/* - * This function inspects header fields in |nva| of length |nvlen| and - * records its method in stream->http_flags. - */ -void nghttp3_http_record_request_method(nghttp3_stream *stream, - const nghttp3_nv *nva, size_t nvlen); - -#endif /* NGHTTP3_HTTP_H */ diff --git a/deps/nghttp3/lib/nghttp3_idtr.c b/deps/nghttp3/lib/nghttp3_idtr.c deleted file mode 100644 index cd8fd82e6b2..00000000000 --- a/deps/nghttp3/lib/nghttp3_idtr.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 "nghttp3_idtr.h" - -#include <assert.h> - -int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) { - int rv; - - rv = nghttp3_gaptr_init(&idtr->gap, mem); - if (rv != 0) { - return rv; - } - - idtr->server = server; - idtr->mem = mem; - - return 0; -} - -void nghttp3_idtr_free(nghttp3_idtr *idtr) { - if (idtr == NULL) { - return; - } - - nghttp3_gaptr_free(&idtr->gap); -} - -/* - * id_from_stream_id translates |stream_id| to id space used by - * nghttp3_idtr. - */ -static uint64_t id_from_stream_id(int64_t stream_id) { - return (uint64_t)(stream_id >> 2); -} - -int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id) { - uint64_t q; - - assert((idtr->server && (stream_id % 2)) || - (!idtr->server && (stream_id % 2)) == 0); - - q = id_from_stream_id(stream_id); - - if (nghttp3_gaptr_is_pushed(&idtr->gap, q, 1)) { - return NGHTTP3_ERR_STREAM_IN_USE; - } - - return nghttp3_gaptr_push(&idtr->gap, q, 1); -} - -int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id) { - uint64_t q; - - assert((idtr->server && (stream_id % 2)) || - (!idtr->server && (stream_id % 2)) == 0); - - q = id_from_stream_id(stream_id); - - return nghttp3_gaptr_is_pushed(&idtr->gap, q, 1); -} - -uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr) { - return nghttp3_gaptr_first_gap_offset(&idtr->gap); -} diff --git a/deps/nghttp3/lib/nghttp3_idtr.h b/deps/nghttp3/lib/nghttp3_idtr.h deleted file mode 100644 index ba2808a11f5..00000000000 --- a/deps/nghttp3/lib/nghttp3_idtr.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 NGHTTP3_IDTR_H -#define NGHTTP3_IDTR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_mem.h" -#include "nghttp3_gaptr.h" - -/* - * nghttp3_idtr tracks the usage of stream ID. - */ -typedef struct { - /* gap maintains the range of ID which is not used yet. Initially, - its range is [0, UINT64_MAX). */ - nghttp3_gaptr gap; - /* server is nonzero if this object records server initiated stream - ID. */ - int server; - /* mem is custom memory allocator */ - const nghttp3_mem *mem; -} nghttp3_idtr; - -/* - * nghttp3_idtr_init initializes |idtr|. |chunk| is the size of buffer - * per chunk. - * - * If this object records server initiated ID (even number), set - * |server| to nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem); - -/* - * nghttp3_idtr_free frees resources allocated for |idtr|. - */ -void nghttp3_idtr_free(nghttp3_idtr *idtr); - -/* - * nghttp3_idtr_open claims that |stream_id| is in used. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP3_ERR_STREAM_IN_USE - * ID has already been used. - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id); - -/* - * nghttp3_idtr_open tells whether ID |stream_id| is in used or not. - * - * It returns nonzero if |stream_id| is used. - */ -int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id); - -/* - * nghttp3_idtr_first_gap returns the first id of first gap. If there - * is no gap, it returns UINT64_MAX. The returned id is an id space - * used in this object internally, and not stream ID. - */ -uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr); - -#endif /* NGHTTP3_IDTR_H */ diff --git a/deps/nghttp3/lib/nghttp3_ksl.c b/deps/nghttp3/lib/nghttp3_ksl.c deleted file mode 100644 index 6a0f7c1de57..00000000000 --- a/deps/nghttp3/lib/nghttp3_ksl.c +++ /dev/null @@ -1,722 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2018 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 "nghttp3_ksl.h" - -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "nghttp3_macro.h" -#include "nghttp3_mem.h" -#include "nghttp3_range.h" - -static size_t ksl_nodelen(size_t keylen) { - return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; -} - -static size_t ksl_blklen(size_t nodelen) { - return sizeof(nghttp3_ksl_blk) + nodelen * NGHTTP3_KSL_MAX_NBLK - - sizeof(uint64_t); -} - -/* - * ksl_node_set_key sets |key| to |node|. - */ -static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node, - const void *key) { - memcpy(node->key, key, ksl->keylen); -} - -int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, - const nghttp3_mem *mem) { - size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - nghttp3_ksl_blk *head; - - ksl->head = nghttp3_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGHTTP3_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; - ksl->compar = compar; - ksl->keylen = keylen; - ksl->nodelen = nodelen; - ksl->n = 0; - ksl->mem = mem; - - head = ksl->head; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; - - return 0; -} - -/* - * ksl_free_blk frees |blk| recursively. - */ -static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { - size_t i; - - if (!blk->leaf) { - for (i = 0; i < blk->n; ++i) { - ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk); - } - } - - nghttp3_mem_free(ksl->mem, blk); -} - -void nghttp3_ksl_free(nghttp3_ksl *ksl) { - if (!ksl) { - return; - } - - ksl_free_blk(ksl, ksl->head); -} - -/* - * ksl_split_blk splits |blk| into 2 nghttp3_ksl_blk objects. The new - * nghttp3_ksl_blk is always the "right" block. - * - * It returns the pointer to the nghttp3_ksl_blk created which is the - * located at the right of |blk|, or NULL which indicates out of - * memory error. - */ -static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { - nghttp3_ksl_blk *rblk; - - rblk = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); - if (rblk == NULL) { - return NULL; - } - - rblk->next = blk->next; - blk->next = rblk; - if (rblk->next) { - rblk->next->prev = rblk; - } else if (ksl->back == blk) { - ksl->back = rblk; - } - rblk->prev = blk; - rblk->leaf = blk->leaf; - - rblk->n = blk->n / 2; - - memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), - ksl->nodelen * rblk->n); - - blk->n -= rblk->n; - - assert(blk->n >= NGHTTP3_KSL_MIN_NBLK); - assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK); - - return rblk; -} - -/* - * ksl_split_node splits a node included in |blk| at the position |i| - * into 2 adjacent nodes. The new node is always inserted at the - * position |i+1|. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -static int ksl_split_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { - nghttp3_ksl_node *node; - nghttp3_ksl_blk *lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk, *rblk; - - rblk = ksl_split_blk(ksl, lblk); - if (rblk == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - memmove(blk->nodes + (i + 2) * ksl->nodelen, - blk->nodes + (i + 1) * ksl->nodelen, - ksl->nodelen * (blk->n - (i + 1))); - - node = nghttp3_ksl_nth_node(ksl, blk, i + 1); - node->blk = rblk; - ++blk->n; - ksl_node_set_key(ksl, node, - nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); - - node = nghttp3_ksl_nth_node(ksl, blk, i); - ksl_node_set_key(ksl, node, - nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - - return 0; -} - -/* - * ksl_split_head splits a head (root) block. It increases the height - * of skip list by 1. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -static int ksl_split_head(nghttp3_ksl *ksl) { - nghttp3_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; - nghttp3_ksl_node *node; - - rblk = ksl_split_blk(ksl, ksl->head); - if (rblk == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - lblk = ksl->head; - - nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); - if (nhead == NULL) { - nghttp3_mem_free(ksl->mem, rblk); - return NGHTTP3_ERR_NOMEM; - } - nhead->next = nhead->prev = NULL; - nhead->n = 2; - nhead->leaf = 0; - - node = nghttp3_ksl_nth_node(ksl, nhead, 0); - ksl_node_set_key(ksl, node, - nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - node->blk = lblk; - - node = nghttp3_ksl_nth_node(ksl, nhead, 1); - ksl_node_set_key(ksl, node, - nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); - node->blk = rblk; - - ksl->head = nhead; - - return 0; -} - -/* - * insert_node inserts a node whose key is |key| with the associated - * |data| at the index of |i|. This function assumes that the number - * of nodes contained by |blk| is strictly less than - * NGHTTP3_KSL_MAX_NBLK. - */ -static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, - const nghttp3_ksl_key *key, void *data) { - nghttp3_ksl_node *node; - - assert(blk->n < NGHTTP3_KSL_MAX_NBLK); - - memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, - ksl->nodelen * (blk->n - i)); - - node = nghttp3_ksl_nth_node(ksl, blk, i); - ksl_node_set_key(ksl, node, key); - node->data = data; - - ++blk->n; -} - -static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, - const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar) { - nghttp3_ssize left = -1, right = (nghttp3_ssize)blk->n, mid; - nghttp3_ksl_node *node; - - while (right - left > 1) { - mid = (left + right) / 2; - node = nghttp3_ksl_nth_node(ksl, blk, (size_t)mid); - if (compar((nghttp3_ksl_key *)node->key, key)) { - left = mid; - } else { - right = mid; - } - } - - return (size_t)right; -} - -int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, - const nghttp3_ksl_key *key, void *data) { - nghttp3_ksl_blk *blk = ksl->head; - nghttp3_ksl_node *node; - size_t i; - int rv; - - if (blk->n == NGHTTP3_KSL_MAX_NBLK) { - rv = ksl_split_head(ksl); - if (rv != 0) { - return rv; - } - blk = ksl->head; - } - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (blk->leaf) { - if (i < blk->n && - !ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) { - if (it) { - *it = nghttp3_ksl_end(ksl); - } - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - ksl_insert_node(ksl, blk, i, key, data); - ++ksl->n; - if (it) { - nghttp3_ksl_it_init(it, ksl, blk, i); - } - return 0; - } - - if (i == blk->n) { - /* This insertion extends the largest key in this subtree. */ - for (; !blk->leaf;) { - node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1); - if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { - rv = ksl_split_node(ksl, blk, blk->n - 1); - if (rv != 0) { - return rv; - } - node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1); - } - ksl_node_set_key(ksl, node, key); - blk = node->blk; - } - ksl_insert_node(ksl, blk, blk->n, key, data); - ++ksl->n; - if (it) { - nghttp3_ksl_it_init(it, ksl, blk, blk->n - 1); - } - return 0; - } - - node = nghttp3_ksl_nth_node(ksl, blk, i); - - if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { - rv = ksl_split_node(ksl, blk, i); - if (rv != 0) { - return rv; - } - if (ksl->compar((nghttp3_ksl_key *)node->key, key)) { - node = nghttp3_ksl_nth_node(ksl, blk, i + 1); - if (ksl->compar((nghttp3_ksl_key *)node->key, key)) { - ksl_node_set_key(ksl, node, key); - } - } - } - - blk = node->blk; - } -} - -/* - * ksl_remove_node removes the node included in |blk| at the index of - * |i|. - */ -static void ksl_remove_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { - memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, - ksl->nodelen * (blk->n - (i + 1))); - - --blk->n; -} - -/* - * ksl_merge_node merges 2 nodes which are the nodes at the index of - * |i| and |i + 1|. - * - * If |blk| is the direct descendant of head (root) block and the head - * block contains just 2 nodes, the merged block becomes head block, - * which decreases the height of |ksl| by 1. - * - * This function returns the pointer to the merged block. - */ -static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, - size_t i) { - nghttp3_ksl_blk *lblk, *rblk; - - assert(i + 1 < blk->n); - - lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk; - rblk = nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk; - - assert(lblk->n + rblk->n < NGHTTP3_KSL_MAX_NBLK); - - memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, - ksl->nodelen * rblk->n); - - lblk->n += rblk->n; - lblk->next = rblk->next; - if (lblk->next) { - lblk->next->prev = lblk; - } else if (ksl->back == rblk) { - ksl->back = lblk; - } - - nghttp3_mem_free(ksl->mem, rblk); - - if (ksl->head == blk && blk->n == 2) { - nghttp3_mem_free(ksl->mem, ksl->head); - ksl->head = lblk; - } else { - ksl_remove_node(ksl, blk, i + 1); - ksl_node_set_key(ksl, nghttp3_ksl_nth_node(ksl, blk, i), - nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - } - - return lblk; -} - -/* - * ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to - * blk->nodes[i - 1]->blk->nodes. - */ -static void ksl_shift_left(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { - nghttp3_ksl_node *lnode, *rnode, *dest, *src; - - assert(i > 0); - - lnode = nghttp3_ksl_nth_node(ksl, blk, i - 1); - rnode = nghttp3_ksl_nth_node(ksl, blk, i); - - assert(lnode->blk->n < NGHTTP3_KSL_MAX_NBLK); - assert(rnode->blk->n > NGHTTP3_KSL_MIN_NBLK); - - dest = nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n); - src = nghttp3_ksl_nth_node(ksl, rnode->blk, 0); - - memcpy(dest, src, ksl->nodelen); - ksl_node_set_key(ksl, lnode, dest->key); - ++lnode->blk->n; - - --rnode->blk->n; - memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen, - ksl->nodelen * rnode->blk->n); -} - -/* - * ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to - * blk->nodes[i + 1]->blk->nodes. - */ -static void ksl_shift_right(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { - nghttp3_ksl_node *lnode, *rnode, *dest, *src; - - assert(i < blk->n - 1); - - lnode = nghttp3_ksl_nth_node(ksl, blk, i); - rnode = nghttp3_ksl_nth_node(ksl, blk, i + 1); - - assert(lnode->blk->n > NGHTTP3_KSL_MIN_NBLK); - assert(rnode->blk->n < NGHTTP3_KSL_MAX_NBLK); - - memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes, - ksl->nodelen * rnode->blk->n); - ++rnode->blk->n; - - dest = nghttp3_ksl_nth_node(ksl, rnode->blk, 0); - src = nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); - - memcpy(dest, src, ksl->nodelen); - - --lnode->blk->n; - ksl_node_set_key( - ksl, lnode, - nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); -} - -/* - * key_equal returns nonzero if |lhs| and |rhs| are equal using the - * function |compar|. - */ -static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs, - const nghttp3_ksl_key *rhs) { - return !compar(lhs, rhs) && !compar(rhs, lhs); -} - -int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, - const nghttp3_ksl_key *key) { - nghttp3_ksl_blk *blk = ksl->head; - nghttp3_ksl_node *node; - size_t i; - - if (!blk->leaf && blk->n == 2 && - nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK && - nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) { - blk = ksl_merge_node(ksl, ksl->head, 0); - } - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (i == blk->n) { - if (it) { - *it = nghttp3_ksl_end(ksl); - } - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (blk->leaf) { - if (ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) { - if (it) { - *it = nghttp3_ksl_end(ksl); - } - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - ksl_remove_node(ksl, blk, i); - --ksl->n; - if (it) { - if (blk->n == i && blk->next) { - nghttp3_ksl_it_init(it, ksl, blk->next, 0); - } else { - nghttp3_ksl_it_init(it, ksl, blk, i); - } - } - return 0; - } - - node = nghttp3_ksl_nth_node(ksl, blk, i); - - if (node->blk->n == NGHTTP3_KSL_MIN_NBLK) { - if (i > 0 && nghttp3_ksl_nth_node(ksl, blk, i - 1)->blk->n > - NGHTTP3_KSL_MIN_NBLK) { - ksl_shift_right(ksl, blk, i - 1); - blk = node->blk; - } else if (i + 1 < blk->n && - nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk->n > - NGHTTP3_KSL_MIN_NBLK) { - ksl_shift_left(ksl, blk, i + 1); - blk = node->blk; - } else if (i > 0) { - blk = ksl_merge_node(ksl, blk, i - 1); - } else { - assert(i + 1 < blk->n); - blk = ksl_merge_node(ksl, blk, i); - } - } else { - blk = node->blk; - } - } -} - -nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl, - const nghttp3_ksl_key *key) { - nghttp3_ksl_blk *blk = ksl->head; - nghttp3_ksl_it it; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (blk->leaf) { - if (i == blk->n && blk->next) { - blk = blk->next; - i = 0; - } - nghttp3_ksl_it_init(&it, ksl, blk, i); - return it; - } - - if (i == blk->n) { - /* This happens if descendant has smaller key. Fast forward to - find last node in this subtree. */ - for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk) - ; - if (blk->next) { - blk = blk->next; - i = 0; - } else { - i = blk->n; - } - nghttp3_ksl_it_init(&it, ksl, blk, i); - return it; - } - blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk; - } -} - -nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, - const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar) { - nghttp3_ksl_blk *blk = ksl->head; - nghttp3_ksl_it it; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, key, compar); - - if (blk->leaf) { - if (i == blk->n && blk->next) { - blk = blk->next; - i = 0; - } - nghttp3_ksl_it_init(&it, ksl, blk, i); - return it; - } - - if (i == blk->n) { - /* This happens if descendant has smaller key. Fast forward to - find last node in this subtree. */ - for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk) - ; - if (blk->next) { - blk = blk->next; - i = 0; - } else { - i = blk->n; - } - nghttp3_ksl_it_init(&it, ksl, blk, i); - return it; - } - blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk; - } -} - -void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, - const nghttp3_ksl_key *new_key) { - nghttp3_ksl_blk *blk = ksl->head; - nghttp3_ksl_node *node; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, old_key, ksl->compar); - - assert(i < blk->n); - node = nghttp3_ksl_nth_node(ksl, blk, i); - - if (blk->leaf) { - assert(key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key)); - ksl_node_set_key(ksl, node, new_key); - return; - } - - if (key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key) || - ksl->compar((nghttp3_ksl_key *)node->key, new_key)) { - ksl_node_set_key(ksl, node, new_key); - } - - blk = node->blk; - } -} - -static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) { - size_t i; - nghttp3_ksl_node *node; - - fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); - - if (blk->leaf) { - for (i = 0; i < blk->n; ++i) { - node = nghttp3_ksl_nth_node(ksl, blk, i); - fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); - } - fprintf(stderr, "\n"); - return; - } - - for (i = 0; i < blk->n; ++i) { - ksl_print(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk, level + 1); - } -} - -size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; } - -void nghttp3_ksl_clear(nghttp3_ksl *ksl) { - size_t i; - nghttp3_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, ksl->head, i)->blk); - } - } - - ksl->front = ksl->back = ksl->head; - ksl->n = 0; - - head = ksl->head; - - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; -} - -void nghttp3_ksl_print(nghttp3_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } - -nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) { - nghttp3_ksl_it it; - nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); - return it; -} - -nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) { - nghttp3_ksl_it it; - nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); - return it; -} - -void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, - nghttp3_ksl_blk *blk, size_t i) { - it->ksl = ksl; - it->blk = blk; - it->i = i; -} - -void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) { - assert(it->i < it->blk->n); - return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data; -} - -void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) { - assert(!nghttp3_ksl_it_begin(it)); - - if (it->i == 0) { - it->blk = it->blk->prev; - it->i = it->blk->n - 1; - } else { - --it->i; - } -} - -int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) { - return it->i == 0 && it->blk->prev == NULL; -} - -int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, - const nghttp3_ksl_key *rhs) { - const nghttp3_range *a = lhs, *b = rhs; - return a->begin < b->begin; -} - -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(a->begin, b->begin) < nghttp3_min(a->end, b->end)); -} diff --git a/deps/nghttp3/lib/nghttp3_ksl.h b/deps/nghttp3/lib/nghttp3_ksl.h deleted file mode 100644 index aeb77eea575..00000000000 --- a/deps/nghttp3/lib/nghttp3_ksl.h +++ /dev/null @@ -1,335 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2018 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 NGHTTP3_KSL_H -#define NGHTTP3_KSL_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <stdlib.h> - -#include <nghttp3/nghttp3.h> - -/* - * Skip List using single key instead of range. - */ - -#define NGHTTP3_KSL_DEGR 16 -/* NGHTTP3_KSL_MAX_NBLK is the maximum number of nodes which a single - block can contain. */ -#define NGHTTP3_KSL_MAX_NBLK (2 * NGHTTP3_KSL_DEGR - 1) -/* NGHTTP3_KSL_MIN_NBLK is the minimum number of nodes which a single - block other than root must contains. */ -#define NGHTTP3_KSL_MIN_NBLK (NGHTTP3_KSL_DEGR - 1) - -/* - * nghttp3_ksl_key represents key in nghttp3_ksl. - */ -typedef void nghttp3_ksl_key; - -struct nghttp3_ksl_node; -typedef struct nghttp3_ksl_node nghttp3_ksl_node; - -struct nghttp3_ksl_blk; -typedef struct nghttp3_ksl_blk nghttp3_ksl_blk; - -/* - * nghttp3_ksl_node is a node which contains either nghttp3_ksl_blk or - * opaque data. If a node is an internal node, it contains - * nghttp3_ksl_blk. Otherwise, it has data. The key is stored at the - * location starting at key. - */ -struct nghttp3_ksl_node { - union { - nghttp3_ksl_blk *blk; - void *data; - }; - union { - uint64_t align; - /* key is a buffer to include key associated to this node. - Because the length of key is unknown until nghttp3_ksl_init is - called, the actual buffer will be allocated after this - field. */ - uint8_t key[1]; - }; -}; - -/* - * nghttp3_ksl_blk contains nghttp3_ksl_node objects. - */ -struct nghttp3_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - nghttp3_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - nghttp3_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - size_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - int leaf; - union { - uint64_t align; - /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK - nghttp3_ksl_node objects. Because nghttp3_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until nghttp3_ksl_init - is called. */ - uint8_t nodes[1]; - }; -}; - -/* - * nghttp3_ksl_compar is a function type which returns nonzero if key - * |lhs| should be placed before |rhs|. It returns 0 otherwise. - */ -typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs, - const nghttp3_ksl_key *rhs); - -struct nghttp3_ksl; -typedef struct nghttp3_ksl nghttp3_ksl; - -struct nghttp3_ksl_it; -typedef struct nghttp3_ksl_it nghttp3_ksl_it; - -/* - * nghttp3_ksl_it is a forward iterator to iterate nodes. - */ -struct nghttp3_ksl_it { - const nghttp3_ksl *ksl; - nghttp3_ksl_blk *blk; - size_t i; -}; - -/* - * nghttp3_ksl is a deterministic paged skip list. - */ -struct nghttp3_ksl { - /* head points to the root block. */ - nghttp3_ksl_blk *head; - /* front points to the first leaf block. */ - nghttp3_ksl_blk *front; - /* back points to the last leaf block. */ - nghttp3_ksl_blk *back; - nghttp3_ksl_compar compar; - size_t n; - /* keylen is the size of key */ - size_t keylen; - /* nodelen is the actual size of nghttp3_ksl_node including key - storage. */ - size_t nodelen; - const nghttp3_mem *mem; -}; - -/* - * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare - * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, - const nghttp3_mem *mem); - -/* - * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is - * NULL, this function does nothing. It does not free the memory - * region pointed by |ksl| itself. - */ -void nghttp3_ksl_free(nghttp3_ksl *ksl); - -/* - * nghttp3_ksl_insert inserts |key| with its associated |data|. On - * successful insertion, the iterator points to the inserted node is - * stored in |*it|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - * NGHTTP3_ERR_INVALID_ARGUMENT - * |key| already exists. - */ -int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, - const nghttp3_ksl_key *key, void *data); - -/* - * nghttp3_ksl_remove removes the |key| from |ksl|. - * - * This function assigns the iterator to |*it|, which points to the - * node which is located at the right next of the removed node if |it| - * is not NULL. If |key| is not found, no deletion takes place and - * the return value of nghttp3_ksl_end(ksl) is assigned to |*it|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_INVALID_ARGUMENT - * |key| does not exist. - */ -int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, - const nghttp3_ksl_key *key); - -/* - * nghttp3_ksl_lower_bound returns the iterator which points to the - * first node which has the key which is equal to |key| or the last - * node which satisfies !compar(&node->key, key). If there is no such - * node, it returns the iterator which satisfies nghttp3_ksl_it_end(it) - * != 0. - */ -nghttp3_ksl_it nghttp3_ksl_lower_bound(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_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, - const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar); - -/* - * nghttp3_ksl_update_key replaces the key of nodes which has |old_key| - * with |new_key|. |new_key| must be strictly greater than the - * previous node and strictly smaller than the next node. - */ -void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, - const nghttp3_ksl_key *new_key); - -/* - * nghttp3_ksl_begin returns the iterator which points to the first - * node. If there is no node in |ksl|, it returns the iterator which - * satisfies nghttp3_ksl_it_end(it) != 0. - */ -nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl); - -/* - * nghttp3_ksl_end returns the iterator which points to the node - * following the last node. The returned object satisfies - * nghttp3_ksl_it_end(). If there is no node in |ksl|, it returns the - * iterator which satisfies nghttp3_ksl_it_begin(it) != 0. - */ -nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl); - -/* - * nghttp3_ksl_len returns the number of elements stored in |ksl|. - */ -size_t nghttp3_ksl_len(nghttp3_ksl *ksl); - -/* - * nghttp3_ksl_clear removes all elements stored in |ksl|. - */ -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))) - -/* - * nghttp3_ksl_print prints its internal state in stderr. It assumes - * that the key is of type int64_t. This function should be used for - * the debugging purpose only. - */ -void nghttp3_ksl_print(nghttp3_ksl *ksl); - -/* - * nghttp3_ksl_it_init initializes |it|. - */ -void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, - nghttp3_ksl_blk *blk, size_t i); - -/* - * nghttp3_ksl_it_get returns the data associated to the node which - * |it| points to. It is undefined to call this function when - * nghttp3_ksl_it_end(it) returns nonzero. - */ -void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it); - -/* - * 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) - -/* - * nghttp3_ksl_it_prev moves backward the iterator by one. It is - * undefined if this function is called when nghttp3_ksl_it_begin(it) - * returns nonzero. - */ -void nghttp3_ksl_it_prev(nghttp3_ksl_it *it); - -/* - * nghttp3_ksl_it_end returns nonzero if |it| points to the beyond the - * last node. - */ -#define nghttp3_ksl_it_end(IT) \ - ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) - -/* - * nghttp3_ksl_it_begin returns nonzero if |it| points to the first - * node. |it| might satisfy both nghttp3_ksl_it_begin(&it) and - * nghttp3_ksl_it_end(&it) if the skip list has no node. - */ -int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it); - -/* - * nghttp3_ksl_key returns the key of the node which |it| points to. - * 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) - -/* - * 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. - */ -int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, - const nghttp3_ksl_key *rhs); - -/* - * 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. - */ -int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, - const nghttp3_ksl_key *rhs); - -#endif /* NGHTTP3_KSL_H */ diff --git a/deps/nghttp3/lib/nghttp3_macro.h b/deps/nghttp3/lib/nghttp3_macro.h deleted file mode 100644 index 6ee704cc47d..00000000000 --- a/deps/nghttp3/lib/nghttp3_macro.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 NGHTTP3_MACRO_H -#define NGHTTP3_MACRO_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <stddef.h> - -#include <nghttp3/nghttp3.h> - -#define nghttp3_min(A, B) ((A) < (B) ? (A) : (B)) -#define nghttp3_max(A, B) ((A) > (B) ? (A) : (B)) - -#define nghttp3_struct_of(ptr, type, member) \ - ((type *)(void *)((char *)(ptr)-offsetof(type, member))) - -#define nghttp3_arraylen(A) (sizeof(A) / sizeof(*(A))) - -#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) - -#endif /* NGHTTP3_MACRO_H */ diff --git a/deps/nghttp3/lib/nghttp3_map.c b/deps/nghttp3/lib/nghttp3_map.c deleted file mode 100644 index e8eb811566d..00000000000 --- a/deps/nghttp3/lib/nghttp3_map.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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 "nghttp3_map.h" - -#include <string.h> -#include <assert.h> - -#include "nghttp3_conv.h" - -#define INITIAL_TABLE_LENGTH 256 - -int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { - map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; - map->table = - nghttp3_mem_calloc(mem, map->tablelen, sizeof(nghttp3_map_bucket)); - if (map->table == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - map->size = 0; - - return 0; -} - -void nghttp3_map_free(nghttp3_map *map) { - size_t i; - nghttp3_map_bucket *bkt; - - if (!map) { - return; - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } - } - - nghttp3_mem_free(map->mem, map->table); -} - -void nghttp3_map_each_free(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), - void *ptr) { - uint32_t i; - nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - - if (bkt->ptr) { - func(bkt->ptr, ptr); - bkt->ptr = NULL; - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); - continue; - } - - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - func(nghttp3_ksl_it_get(&it), ptr); - } - - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } - } -} - -int nghttp3_map_each(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), - void *ptr) { - int rv; - uint32_t i; - nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - - if (bkt->ptr) { - rv = func(bkt->ptr, ptr); - if (rv != 0) { - return rv; - } - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); - continue; - } - - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - rv = func(nghttp3_ksl_it_get(&it), ptr); - if (rv != 0) { - return rv; - } - } - } - } - return 0; -} - -void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) { - entry->key = key; - entry->next = NULL; -} - -/* FNV1a hash */ -static uint32_t hash(key_type key, uint32_t mod) { - uint8_t *p, *end; - uint32_t h = 0x811C9DC5u; - - key = nghttp3_htonl64(key); - p = (uint8_t *)&key; - end = p + sizeof(key_type); - - for (; p != end;) { - h ^= *p++; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } - - return h & (mod - 1); -} - -static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) { - return *(key_type *)lhs < *(key_type *)rhs; -} - -static int map_insert(nghttp3_map *map, nghttp3_map_bucket *table, - uint32_t tablelen, nghttp3_map_entry *entry) { - uint32_t h = hash(entry->key, tablelen); - nghttp3_map_bucket *bkt = &table[h]; - const nghttp3_mem *mem = map->mem; - int rv; - - if (bkt->ptr == NULL && - (bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) { - bkt->ptr = entry; - return 0; - } - - if (!bkt->ksl) { - bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl)); - if (bkt->ksl == NULL) { - return NGHTTP3_ERR_NOMEM; - } - nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem); - } - - if (bkt->ptr) { - rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); - if (rv != 0) { - return rv; - } - - bkt->ptr = NULL; - } - - return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry); -} - -/* new_tablelen must be power of 2 */ -static int map_resize(nghttp3_map *map, uint32_t new_tablelen) { - uint32_t i; - nghttp3_map_bucket *new_table; - nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; - int rv; - - new_table = - nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket)); - if (new_table == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - - if (bkt->ptr) { - rv = map_insert(map, new_table, new_tablelen, bkt->ptr); - if (rv != 0) { - goto fail; - } - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); - continue; - } - - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - rv = map_insert(map, new_table, new_tablelen, nghttp3_ksl_it_get(&it)); - if (rv != 0) { - goto fail; - } - } - } - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } - } - - nghttp3_mem_free(map->mem, map->table); - map->tablelen = new_tablelen; - map->table = new_table; - - return 0; - -fail: - for (i = 0; i < new_tablelen; ++i) { - bkt = &new_table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } - } - - return rv; -} - -int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) { - int rv; - - /* Load factor is 0.75 */ - if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2); - if (rv != 0) { - return rv; - } - } - rv = map_insert(map, map->table, map->tablelen, new_entry); - if (rv != 0) { - return rv; - } - ++map->size; - return 0; -} - -nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key) { - nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - nghttp3_ksl_it it; - - if (bkt->ptr) { - if (bkt->ptr->key == key) { - return bkt->ptr; - } - return NULL; - } - - if (bkt->ksl) { - it = nghttp3_ksl_lower_bound(bkt->ksl, &key); - if (nghttp3_ksl_it_end(&it) || - *(key_type *)nghttp3_ksl_it_key(&it) != key) { - return NULL; - } - return nghttp3_ksl_it_get(&it); - } - - return NULL; -} - -int nghttp3_map_remove(nghttp3_map *map, key_type key) { - nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - int rv; - - if (bkt->ptr) { - if (bkt->ptr->key == key) { - bkt->ptr = NULL; - --map->size; - return 0; - } - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (bkt->ksl) { - rv = nghttp3_ksl_remove(bkt->ksl, NULL, &key); - if (rv != 0) { - return rv; - } - --map->size; - return 0; - } - - return NGHTTP3_ERR_INVALID_ARGUMENT; -} - -void nghttp3_map_clear(nghttp3_map *map) { - uint32_t i; - nghttp3_map_bucket *bkt; - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - bkt->ptr = NULL; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } - } - - map->size = 0; -} - -size_t nghttp3_map_size(nghttp3_map *map) { return map->size; } diff --git a/deps/nghttp3/lib/nghttp3_map.h b/deps/nghttp3/lib/nghttp3_map.h deleted file mode 100644 index 8a007119709..00000000000 --- a/deps/nghttp3/lib/nghttp3_map.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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. - */ -#ifndef NGHTTP3_MAP_H -#define NGHTTP3_MAP_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_mem.h" -#include "nghttp3_ksl.h" - -/* Implementation of unordered map */ - -typedef uint64_t key_type; - -typedef struct nghttp3_map_entry { - struct nghttp3_map_entry *next; - key_type key; -} nghttp3_map_entry; - -typedef struct nghttp3_map_bucket { - nghttp3_map_entry *ptr; - nghttp3_ksl *ksl; -} nghttp3_map_bucket; - -typedef struct { - nghttp3_map_bucket *table; - const nghttp3_mem *mem; - size_t size; - uint32_t tablelen; -} nghttp3_map; - -/* - * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory - */ -int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); - -/* - * Deallocates any resources allocated for |map|. The stored entries - * are not freed by this function. Use nghttp3_map_each_free() to free - * each entries. - */ -void nghttp3_map_free(nghttp3_map *map); - -/* - * Deallocates each entries using |func| function and any resources - * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as - * send argument. The return value of the |func| will be ignored. - */ -void nghttp3_map_each_free(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), - void *ptr); - -/* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_INVALID_ARGUMENT - * The item associated by |key| already exists. - * NGHTTP3_ERR_NOMEM - * Out of memory - */ -int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry); - -/* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. - */ -nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key); - -/* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. - */ -int nghttp3_map_remove(nghttp3_map *map, key_type key); - -/* - * Removes all entries from |map|. - */ -void nghttp3_map_clear(nghttp3_map *map); - -/* - * Returns the number of items stored in the map |map|. - */ -size_t nghttp3_map_size(nghttp3_map *map); - -/* - * Applies the function |func| to each entry in the |map| with the - * optional user supplied pointer |ptr|. - * - * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the - * |func| for further entries and return the return value of the - * |func| immediately. Thus, this function returns 0 if all the - * invocations of the |func| return 0, or nonzero value which the last - * invocation of |func| returns. - * - * Don't use this function to free each entry. Use - * nghttp3_map_each_free() instead. - */ -int nghttp3_map_each(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), - void *ptr); - -#endif /* NGHTTP3_MAP_H */ diff --git a/deps/nghttp3/lib/nghttp3_mem.c b/deps/nghttp3/lib/nghttp3_mem.c deleted file mode 100644 index e5f93f10bda..00000000000 --- a/deps/nghttp3/lib/nghttp3_mem.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2014 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 "nghttp3_mem.h" - -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; - - return malloc(size); -} - -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; - - free(ptr); -} - -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; - - return calloc(nmemb, size); -} - -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; - - return realloc(ptr, size); -} - -static nghttp3_mem mem_default = {NULL, default_malloc, default_free, - default_calloc, default_realloc}; - -const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; } - -void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); -} - -void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, - void *mem_user_data) { - free_func(ptr, mem_user_data); -} - -void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); -} - -void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); -} diff --git a/deps/nghttp3/lib/nghttp3_mem.h b/deps/nghttp3/lib/nghttp3_mem.h deleted file mode 100644 index 55ef86b4f92..00000000000 --- a/deps/nghttp3/lib/nghttp3_mem.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2014 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. - */ -#ifndef NGHTTP3_MEM_H -#define NGHTTP3_MEM_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -/* Convenient wrapper functions to call allocator function in - |mem|. */ -void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size); -void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr); -void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, - void *mem_user_data); -void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size); -void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size); - -#endif /* NGHTTP3_MEM_H */ diff --git a/deps/nghttp3/lib/nghttp3_pq.c b/deps/nghttp3/lib/nghttp3_pq.c deleted file mode 100644 index 5d09050ae63..00000000000 --- a/deps/nghttp3/lib/nghttp3_pq.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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 "nghttp3_pq.h" - -#include <assert.h> - -#include "nghttp3_macro.h" - -void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, - const nghttp3_mem *mem) { - pq->mem = mem; - pq->capacity = 0; - pq->q = NULL; - pq->length = 0; - pq->less = less; -} - -void nghttp3_pq_free(nghttp3_pq *pq) { - nghttp3_mem_free(pq->mem, pq->q); - pq->q = NULL; -} - -static void swap(nghttp3_pq *pq, size_t i, size_t j) { - nghttp3_pq_entry *a = pq->q[i]; - nghttp3_pq_entry *b = pq->q[j]; - - pq->q[i] = b; - b->index = i; - pq->q[j] = a; - a->index = j; -} - -static void bubble_up(nghttp3_pq *pq, size_t index) { - size_t parent; - while (index != 0) { - parent = (index - 1) / 2; - if (!pq->less(pq->q[index], pq->q[parent])) { - return; - } - swap(pq, parent, index); - index = parent; - } -} - -int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item) { - if (pq->capacity <= pq->length) { - void *nq; - size_t ncapacity; - - ncapacity = nghttp3_max(4, (pq->capacity * 2)); - - nq = nghttp3_mem_realloc(pq->mem, pq->q, - ncapacity * sizeof(nghttp3_pq_entry *)); - if (nq == NULL) { - return NGHTTP3_ERR_NOMEM; - } - pq->capacity = ncapacity; - pq->q = nq; - } - pq->q[pq->length] = item; - item->index = pq->length; - ++pq->length; - bubble_up(pq, pq->length - 1); - return 0; -} - -nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq) { - assert(pq->length); - return pq->q[0]; -} - -static void bubble_down(nghttp3_pq *pq, size_t index) { - size_t i, j, minindex; - for (;;) { - j = index * 2 + 1; - minindex = index; - for (i = 0; i < 2; ++i, ++j) { - if (j >= pq->length) { - break; - } - if (pq->less(pq->q[j], pq->q[minindex])) { - minindex = j; - } - } - if (minindex == index) { - return; - } - swap(pq, index, minindex); - index = minindex; - } -} - -void nghttp3_pq_pop(nghttp3_pq *pq) { - if (pq->length > 0) { - pq->q[0] = pq->q[pq->length - 1]; - pq->q[0]->index = 0; - --pq->length; - bubble_down(pq, 0); - } -} - -void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item) { - assert(pq->q[item->index] == item); - - if (item->index == 0) { - nghttp3_pq_pop(pq); - return; - } - - if (item->index == pq->length - 1) { - --pq->length; - return; - } - - pq->q[item->index] = pq->q[pq->length - 1]; - pq->q[item->index]->index = item->index; - --pq->length; - - if (pq->less(item, pq->q[item->index])) { - bubble_down(pq, item->index); - } else { - bubble_up(pq, item->index); - } -} - -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/nghttp3/lib/nghttp3_pq.h b/deps/nghttp3/lib/nghttp3_pq.h deleted file mode 100644 index e80b1bc43b9..00000000000 --- a/deps/nghttp3/lib/nghttp3_pq.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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. - */ -#ifndef NGHTTP3_PQ_H -#define NGHTTP3_PQ_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_mem.h" - -/* Implementation of priority queue */ - -/* NGHTTP3_PQ_BAD_INDEX is the priority queue index which indicates - that an entry is not queued. Assigning this value to - nghttp3_pq_entry.index can check that the entry is queued or not. */ -#define NGHTTP3_PQ_BAD_INDEX SIZE_MAX - -typedef struct { - size_t index; -} nghttp3_pq_entry; - -/* "less" function, return nonzero if |lhs| is less than |rhs|. */ -typedef int (*nghttp3_less)(const nghttp3_pq_entry *lhs, - const nghttp3_pq_entry *rhs); - -typedef struct { - /* The pointer to the pointer to the item stored */ - nghttp3_pq_entry **q; - /* Memory allocator */ - const nghttp3_mem *mem; - /* The number of items stored */ - size_t length; - /* The maximum number of items this pq can store. This is - automatically extended when length is reached to this value. */ - size_t capacity; - /* The less function between items */ - nghttp3_less less; -} nghttp3_pq; - -/* - * Initializes priority queue |pq| with compare function |cmp|. - */ -void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, const nghttp3_mem *mem); - -/* - * Deallocates any resources allocated for |pq|. The stored items are - * not freed by this function. - */ -void nghttp3_pq_free(nghttp3_pq *pq); - -/* - * Adds |item| to the priority queue |pq|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item); - -/* - * Returns item at the top of the queue |pq|. It is undefined if the - * queue is empty. - */ -nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq); - -/* - * Pops item at the top of the queue |pq|. The popped item is not - * freed by this function. - */ -void nghttp3_pq_pop(nghttp3_pq *pq); - -/* - * Returns nonzero if the queue |pq| is empty. - */ -int nghttp3_pq_empty(const nghttp3_pq *pq); - -/* - * Returns the number of items in the queue |pq|. - */ -size_t nghttp3_pq_size(const nghttp3_pq *pq); - -typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); - -/* - * Applys |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); - -/* - * Removes |item| from priority queue. - */ -void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item); - -void nghttp3_pq_clear(nghttp3_pq *pq); - -#endif /* NGHTTP3_PQ_H */ diff --git a/deps/nghttp3/lib/nghttp3_qpack.c b/deps/nghttp3/lib/nghttp3_qpack.c deleted file mode 100644 index d74e29f6fec..00000000000 --- a/deps/nghttp3/lib/nghttp3_qpack.c +++ /dev/null @@ -1,4096 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2013 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 "nghttp3_qpack.h" - -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "nghttp3_str.h" -#include "nghttp3_macro.h" -#include "nghttp3_debug.h" - -/* NGHTTP3_QPACK_MAX_QPACK_STREAMS is the maximum number of concurrent - nghttp3_qpack_stream object to handle a client which never cancel - or acknowledge header block. After this limit, encoder stops using - dynamic table. */ -#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 } - -/* Generated by mkstatichdtbl.py */ -static nghttp3_qpack_static_entry token_stable[] = { - MAKE_STATIC_ENT(0, NGHTTP3_QPACK_TOKEN__AUTHORITY, 3153725150u), - MAKE_STATIC_ENT(15, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), - MAKE_STATIC_ENT(16, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), - MAKE_STATIC_ENT(17, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), - MAKE_STATIC_ENT(18, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), - MAKE_STATIC_ENT(19, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), - MAKE_STATIC_ENT(20, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), - MAKE_STATIC_ENT(21, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), - MAKE_STATIC_ENT(1, NGHTTP3_QPACK_TOKEN__PATH, 3292848686u), - MAKE_STATIC_ENT(22, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u), - MAKE_STATIC_ENT(23, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u), - MAKE_STATIC_ENT(24, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(25, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(26, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(27, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(28, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(63, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(64, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(65, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(66, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(67, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(68, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(69, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(70, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(71, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), - MAKE_STATIC_ENT(29, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u), - MAKE_STATIC_ENT(30, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u), - MAKE_STATIC_ENT(31, NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING, 3379649177u), - MAKE_STATIC_ENT(72, NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE, 1979086614u), - MAKE_STATIC_ENT(32, NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES, 1713753958u), - MAKE_STATIC_ENT(73, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS, - 901040780u), - MAKE_STATIC_ENT(74, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS, - 901040780u), - MAKE_STATIC_ENT(33, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, - 1524311232u), - MAKE_STATIC_ENT(34, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, - 1524311232u), - MAKE_STATIC_ENT(75, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, - 1524311232u), - MAKE_STATIC_ENT(76, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, - 2175229868u), - MAKE_STATIC_ENT(77, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, - 2175229868u), - MAKE_STATIC_ENT(78, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, - 2175229868u), - MAKE_STATIC_ENT(35, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN, - 2710797292u), - MAKE_STATIC_ENT(79, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS, - 2449824425u), - MAKE_STATIC_ENT(80, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS, - 3599549072u), - MAKE_STATIC_ENT(81, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD, - 2417078055u), - MAKE_STATIC_ENT(82, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD, - 2417078055u), - MAKE_STATIC_ENT(2, NGHTTP3_QPACK_TOKEN_AGE, 742476188u), - MAKE_STATIC_ENT(83, NGHTTP3_QPACK_TOKEN_ALT_SVC, 2148877059u), - MAKE_STATIC_ENT(84, NGHTTP3_QPACK_TOKEN_AUTHORIZATION, 2436257726u), - MAKE_STATIC_ENT(36, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), - MAKE_STATIC_ENT(37, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), - MAKE_STATIC_ENT(38, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), - MAKE_STATIC_ENT(39, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), - MAKE_STATIC_ENT(40, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), - MAKE_STATIC_ENT(41, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), - MAKE_STATIC_ENT(3, NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION, 3889184348u), - MAKE_STATIC_ENT(42, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u), - MAKE_STATIC_ENT(43, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u), - MAKE_STATIC_ENT(4, NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH, 1308181789u), - MAKE_STATIC_ENT(85, NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY, - 1569039836u), - MAKE_STATIC_ENT(44, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(45, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(46, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(47, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(48, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(49, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(50, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(51, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(52, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(53, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(54, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), - MAKE_STATIC_ENT(5, NGHTTP3_QPACK_TOKEN_COOKIE, 2007449791u), - MAKE_STATIC_ENT(6, NGHTTP3_QPACK_TOKEN_DATE, 3564297305u), - MAKE_STATIC_ENT(86, NGHTTP3_QPACK_TOKEN_EARLY_DATA, 4080895051u), - MAKE_STATIC_ENT(7, NGHTTP3_QPACK_TOKEN_ETAG, 113792960u), - MAKE_STATIC_ENT(87, NGHTTP3_QPACK_TOKEN_EXPECT_CT, 1183214960u), - MAKE_STATIC_ENT(88, NGHTTP3_QPACK_TOKEN_FORWARDED, 1485178027u), - MAKE_STATIC_ENT(8, NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE, 2213050793u), - MAKE_STATIC_ENT(9, NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH, 2536202615u), - MAKE_STATIC_ENT(89, NGHTTP3_QPACK_TOKEN_IF_RANGE, 2340978238u), - MAKE_STATIC_ENT(10, NGHTTP3_QPACK_TOKEN_LAST_MODIFIED, 3226950251u), - MAKE_STATIC_ENT(11, NGHTTP3_QPACK_TOKEN_LINK, 232457833u), - MAKE_STATIC_ENT(12, NGHTTP3_QPACK_TOKEN_LOCATION, 200649126u), - MAKE_STATIC_ENT(90, NGHTTP3_QPACK_TOKEN_ORIGIN, 3649018447u), - MAKE_STATIC_ENT(91, NGHTTP3_QPACK_TOKEN_PURPOSE, 4212263681u), - MAKE_STATIC_ENT(55, NGHTTP3_QPACK_TOKEN_RANGE, 4208725202u), - MAKE_STATIC_ENT(13, NGHTTP3_QPACK_TOKEN_REFERER, 3969579366u), - MAKE_STATIC_ENT(92, NGHTTP3_QPACK_TOKEN_SERVER, 1085029842u), - MAKE_STATIC_ENT(14, NGHTTP3_QPACK_TOKEN_SET_COOKIE, 1848371000u), - MAKE_STATIC_ENT(56, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, - 4138147361u), - MAKE_STATIC_ENT(57, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, - 4138147361u), - MAKE_STATIC_ENT(58, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, - 4138147361u), - MAKE_STATIC_ENT(93, NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN, 2432297564u), - MAKE_STATIC_ENT(94, NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS, - 2479169413u), - MAKE_STATIC_ENT(95, NGHTTP3_QPACK_TOKEN_USER_AGENT, 606444526u), - MAKE_STATIC_ENT(59, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u), - MAKE_STATIC_ENT(60, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u), - MAKE_STATIC_ENT(61, NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS, - 3644557769u), - MAKE_STATIC_ENT(96, NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR, 2914187656u), - MAKE_STATIC_ENT(97, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u), - MAKE_STATIC_ENT(98, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u), - MAKE_STATIC_ENT(62, NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION, 2501058888u), -}; - -/* Make scalar initialization form of nghttp3_qpack_static_entry */ -#define MAKE_STATIC_HD(N, V, T) \ - { \ - {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \ - } - -static nghttp3_qpack_static_header stable[] = { - MAKE_STATIC_HD(":authority", "", NGHTTP3_QPACK_TOKEN__AUTHORITY), - MAKE_STATIC_HD(":path", "/", NGHTTP3_QPACK_TOKEN__PATH), - MAKE_STATIC_HD("age", "0", NGHTTP3_QPACK_TOKEN_AGE), - MAKE_STATIC_HD("content-disposition", "", - NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION), - MAKE_STATIC_HD("content-length", "0", NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH), - MAKE_STATIC_HD("cookie", "", NGHTTP3_QPACK_TOKEN_COOKIE), - MAKE_STATIC_HD("date", "", NGHTTP3_QPACK_TOKEN_DATE), - MAKE_STATIC_HD("etag", "", NGHTTP3_QPACK_TOKEN_ETAG), - MAKE_STATIC_HD("if-modified-since", "", - NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE), - MAKE_STATIC_HD("if-none-match", "", NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH), - MAKE_STATIC_HD("last-modified", "", NGHTTP3_QPACK_TOKEN_LAST_MODIFIED), - MAKE_STATIC_HD("link", "", NGHTTP3_QPACK_TOKEN_LINK), - MAKE_STATIC_HD("location", "", NGHTTP3_QPACK_TOKEN_LOCATION), - MAKE_STATIC_HD("referer", "", NGHTTP3_QPACK_TOKEN_REFERER), - MAKE_STATIC_HD("set-cookie", "", NGHTTP3_QPACK_TOKEN_SET_COOKIE), - MAKE_STATIC_HD(":method", "CONNECT", NGHTTP3_QPACK_TOKEN__METHOD), - MAKE_STATIC_HD(":method", "DELETE", NGHTTP3_QPACK_TOKEN__METHOD), - MAKE_STATIC_HD(":method", "GET", NGHTTP3_QPACK_TOKEN__METHOD), - MAKE_STATIC_HD(":method", "HEAD", NGHTTP3_QPACK_TOKEN__METHOD), - MAKE_STATIC_HD(":method", "OPTIONS", NGHTTP3_QPACK_TOKEN__METHOD), - MAKE_STATIC_HD(":method", "POST", NGHTTP3_QPACK_TOKEN__METHOD), - MAKE_STATIC_HD(":method", "PUT", NGHTTP3_QPACK_TOKEN__METHOD), - MAKE_STATIC_HD(":scheme", "http", NGHTTP3_QPACK_TOKEN__SCHEME), - MAKE_STATIC_HD(":scheme", "https", NGHTTP3_QPACK_TOKEN__SCHEME), - MAKE_STATIC_HD(":status", "103", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "200", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "304", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "404", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "503", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD("accept", "*/*", NGHTTP3_QPACK_TOKEN_ACCEPT), - MAKE_STATIC_HD("accept", "application/dns-message", - NGHTTP3_QPACK_TOKEN_ACCEPT), - MAKE_STATIC_HD("accept-encoding", "gzip, deflate, br", - NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING), - MAKE_STATIC_HD("accept-ranges", "bytes", NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES), - MAKE_STATIC_HD("access-control-allow-headers", "cache-control", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), - MAKE_STATIC_HD("access-control-allow-headers", "content-type", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), - MAKE_STATIC_HD("access-control-allow-origin", "*", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN), - MAKE_STATIC_HD("cache-control", "max-age=0", - NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), - MAKE_STATIC_HD("cache-control", "max-age=2592000", - NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), - MAKE_STATIC_HD("cache-control", "max-age=604800", - NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), - MAKE_STATIC_HD("cache-control", "no-cache", - NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), - MAKE_STATIC_HD("cache-control", "no-store", - NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), - MAKE_STATIC_HD("cache-control", "public, max-age=31536000", - NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), - MAKE_STATIC_HD("content-encoding", "br", - NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), - MAKE_STATIC_HD("content-encoding", "gzip", - NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), - MAKE_STATIC_HD("content-type", "application/dns-message", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "application/javascript", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "application/json", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "application/x-www-form-urlencoded", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "image/gif", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "image/jpeg", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "image/png", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "text/css", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "text/html; charset=utf-8", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "text/plain", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("content-type", "text/plain;charset=utf-8", - NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), - MAKE_STATIC_HD("range", "bytes=0-", NGHTTP3_QPACK_TOKEN_RANGE), - MAKE_STATIC_HD("strict-transport-security", "max-age=31536000", - NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), - MAKE_STATIC_HD("strict-transport-security", - "max-age=31536000; includesubdomains", - NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), - MAKE_STATIC_HD("strict-transport-security", - "max-age=31536000; includesubdomains; preload", - NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), - MAKE_STATIC_HD("vary", "accept-encoding", NGHTTP3_QPACK_TOKEN_VARY), - MAKE_STATIC_HD("vary", "origin", NGHTTP3_QPACK_TOKEN_VARY), - MAKE_STATIC_HD("x-content-type-options", "nosniff", - NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS), - MAKE_STATIC_HD("x-xss-protection", "1; mode=block", - NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION), - MAKE_STATIC_HD(":status", "100", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "204", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "206", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "302", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "400", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "403", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "421", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "425", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD(":status", "500", NGHTTP3_QPACK_TOKEN__STATUS), - MAKE_STATIC_HD("accept-language", "", NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE), - MAKE_STATIC_HD("access-control-allow-credentials", "FALSE", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), - MAKE_STATIC_HD("access-control-allow-credentials", "TRUE", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), - MAKE_STATIC_HD("access-control-allow-headers", "*", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), - MAKE_STATIC_HD("access-control-allow-methods", "get", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), - MAKE_STATIC_HD("access-control-allow-methods", "get, post, options", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), - MAKE_STATIC_HD("access-control-allow-methods", "options", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), - MAKE_STATIC_HD("access-control-expose-headers", "content-length", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS), - MAKE_STATIC_HD("access-control-request-headers", "content-type", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS), - MAKE_STATIC_HD("access-control-request-method", "get", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), - MAKE_STATIC_HD("access-control-request-method", "post", - NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), - MAKE_STATIC_HD("alt-svc", "clear", NGHTTP3_QPACK_TOKEN_ALT_SVC), - MAKE_STATIC_HD("authorization", "", NGHTTP3_QPACK_TOKEN_AUTHORIZATION), - MAKE_STATIC_HD("content-security-policy", - "script-src 'none'; object-src 'none'; base-uri 'none'", - NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY), - MAKE_STATIC_HD("early-data", "1", NGHTTP3_QPACK_TOKEN_EARLY_DATA), - MAKE_STATIC_HD("expect-ct", "", NGHTTP3_QPACK_TOKEN_EXPECT_CT), - MAKE_STATIC_HD("forwarded", "", NGHTTP3_QPACK_TOKEN_FORWARDED), - MAKE_STATIC_HD("if-range", "", NGHTTP3_QPACK_TOKEN_IF_RANGE), - MAKE_STATIC_HD("origin", "", NGHTTP3_QPACK_TOKEN_ORIGIN), - MAKE_STATIC_HD("purpose", "prefetch", NGHTTP3_QPACK_TOKEN_PURPOSE), - MAKE_STATIC_HD("server", "", NGHTTP3_QPACK_TOKEN_SERVER), - MAKE_STATIC_HD("timing-allow-origin", "*", - NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN), - MAKE_STATIC_HD("upgrade-insecure-requests", "1", - NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS), - MAKE_STATIC_HD("user-agent", "", NGHTTP3_QPACK_TOKEN_USER_AGENT), - MAKE_STATIC_HD("x-forwarded-for", "", NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR), - MAKE_STATIC_HD("x-frame-options", "deny", - NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), - MAKE_STATIC_HD("x-frame-options", "sameorigin", - NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), -}; - -static int memeq(const void *s1, const void *s2, size_t n) { - return n == 0 || memcmp(s1, s2, n) == 0; -} - -/* Generated by genlibtokenlookup.py */ -static int32_t qpack_lookup_token(const uint8_t *name, size_t namelen) { - switch (namelen) { - case 2: - switch (name[1]) { - case 'e': - if (memeq("t", name, 1)) { - return NGHTTP3_QPACK_TOKEN_TE; - } - break; - } - break; - case 3: - switch (name[2]) { - case 'e': - if (memeq("ag", name, 2)) { - return NGHTTP3_QPACK_TOKEN_AGE; - } - break; - } - break; - case 4: - switch (name[3]) { - case 'e': - if (memeq("dat", name, 3)) { - return NGHTTP3_QPACK_TOKEN_DATE; - } - break; - case 'g': - if (memeq("eta", name, 3)) { - return NGHTTP3_QPACK_TOKEN_ETAG; - } - break; - case 'k': - if (memeq("lin", name, 3)) { - return NGHTTP3_QPACK_TOKEN_LINK; - } - break; - case 't': - if (memeq("hos", name, 3)) { - return NGHTTP3_QPACK_TOKEN_HOST; - } - break; - case 'y': - if (memeq("var", name, 3)) { - return NGHTTP3_QPACK_TOKEN_VARY; - } - break; - } - break; - case 5: - switch (name[4]) { - case 'e': - if (memeq("rang", name, 4)) { - return NGHTTP3_QPACK_TOKEN_RANGE; - } - break; - case 'h': - if (memeq(":pat", name, 4)) { - return NGHTTP3_QPACK_TOKEN__PATH; - } - break; - } - break; - case 6: - switch (name[5]) { - case 'e': - if (memeq("cooki", name, 5)) { - return NGHTTP3_QPACK_TOKEN_COOKIE; - } - break; - case 'n': - if (memeq("origi", name, 5)) { - return NGHTTP3_QPACK_TOKEN_ORIGIN; - } - break; - case 'r': - if (memeq("serve", name, 5)) { - return NGHTTP3_QPACK_TOKEN_SERVER; - } - break; - case 't': - if (memeq("accep", name, 5)) { - return NGHTTP3_QPACK_TOKEN_ACCEPT; - } - break; - } - break; - case 7: - switch (name[6]) { - case 'c': - if (memeq("alt-sv", name, 6)) { - return NGHTTP3_QPACK_TOKEN_ALT_SVC; - } - break; - case 'd': - if (memeq(":metho", name, 6)) { - return NGHTTP3_QPACK_TOKEN__METHOD; - } - break; - case 'e': - if (memeq(":schem", name, 6)) { - return NGHTTP3_QPACK_TOKEN__SCHEME; - } - if (memeq("purpos", name, 6)) { - return NGHTTP3_QPACK_TOKEN_PURPOSE; - } - if (memeq("upgrad", name, 6)) { - return NGHTTP3_QPACK_TOKEN_UPGRADE; - } - break; - case 'r': - if (memeq("refere", name, 6)) { - return NGHTTP3_QPACK_TOKEN_REFERER; - } - break; - case 's': - if (memeq(":statu", name, 6)) { - return NGHTTP3_QPACK_TOKEN__STATUS; - } - break; - } - break; - case 8: - switch (name[7]) { - case 'e': - if (memeq("if-rang", name, 7)) { - return NGHTTP3_QPACK_TOKEN_IF_RANGE; - } - break; - case 'n': - if (memeq("locatio", name, 7)) { - return NGHTTP3_QPACK_TOKEN_LOCATION; - } - break; - case 'y': - if (memeq("priorit", name, 7)) { - return NGHTTP3_QPACK_TOKEN_PRIORITY; - } - break; - } - break; - case 9: - switch (name[8]) { - case 'd': - if (memeq("forwarde", name, 8)) { - return NGHTTP3_QPACK_TOKEN_FORWARDED; - } - break; - case 'l': - if (memeq(":protoco", name, 8)) { - return NGHTTP3_QPACK_TOKEN__PROTOCOL; - } - break; - case 't': - if (memeq("expect-c", name, 8)) { - return NGHTTP3_QPACK_TOKEN_EXPECT_CT; - } - break; - } - break; - case 10: - switch (name[9]) { - case 'a': - if (memeq("early-dat", name, 9)) { - return NGHTTP3_QPACK_TOKEN_EARLY_DATA; - } - break; - case 'e': - if (memeq("keep-aliv", name, 9)) { - return NGHTTP3_QPACK_TOKEN_KEEP_ALIVE; - } - if (memeq("set-cooki", name, 9)) { - return NGHTTP3_QPACK_TOKEN_SET_COOKIE; - } - break; - case 'n': - if (memeq("connectio", name, 9)) { - return NGHTTP3_QPACK_TOKEN_CONNECTION; - } - break; - case 't': - if (memeq("user-agen", name, 9)) { - return NGHTTP3_QPACK_TOKEN_USER_AGENT; - } - break; - case 'y': - if (memeq(":authorit", name, 9)) { - return NGHTTP3_QPACK_TOKEN__AUTHORITY; - } - break; - } - break; - case 12: - switch (name[11]) { - case 'e': - if (memeq("content-typ", name, 11)) { - return NGHTTP3_QPACK_TOKEN_CONTENT_TYPE; - } - break; - } - break; - case 13: - switch (name[12]) { - case 'd': - if (memeq("last-modifie", name, 12)) { - return NGHTTP3_QPACK_TOKEN_LAST_MODIFIED; - } - break; - case 'h': - if (memeq("if-none-matc", name, 12)) { - return NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH; - } - break; - case 'l': - if (memeq("cache-contro", name, 12)) { - return NGHTTP3_QPACK_TOKEN_CACHE_CONTROL; - } - break; - case 'n': - if (memeq("authorizatio", name, 12)) { - return NGHTTP3_QPACK_TOKEN_AUTHORIZATION; - } - break; - case 's': - if (memeq("accept-range", name, 12)) { - return NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES; - } - break; - } - break; - case 14: - switch (name[13]) { - case 'h': - if (memeq("content-lengt", name, 13)) { - return NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH; - } - break; - } - break; - case 15: - switch (name[14]) { - case 'e': - if (memeq("accept-languag", name, 14)) { - return NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE; - } - break; - case 'g': - if (memeq("accept-encodin", name, 14)) { - return NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING; - } - break; - case 'r': - if (memeq("x-forwarded-fo", name, 14)) { - return NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR; - } - break; - case 's': - if (memeq("x-frame-option", name, 14)) { - return NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS; - } - break; - } - break; - case 16: - switch (name[15]) { - case 'g': - if (memeq("content-encodin", name, 15)) { - return NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING; - } - break; - case 'n': - if (memeq("proxy-connectio", name, 15)) { - return NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION; - } - if (memeq("x-xss-protectio", name, 15)) { - return NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION; - } - break; - } - break; - case 17: - switch (name[16]) { - case 'e': - if (memeq("if-modified-sinc", name, 16)) { - return NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE; - } - break; - case 'g': - if (memeq("transfer-encodin", name, 16)) { - return NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING; - } - break; - } - break; - case 19: - switch (name[18]) { - case 'n': - if (memeq("content-dispositio", name, 18)) { - return NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION; - } - if (memeq("timing-allow-origi", name, 18)) { - return NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN; - } - break; - } - break; - case 22: - switch (name[21]) { - case 's': - if (memeq("x-content-type-option", name, 21)) { - return NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS; - } - break; - } - break; - case 23: - switch (name[22]) { - case 'y': - if (memeq("content-security-polic", name, 22)) { - return NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY; - } - break; - } - break; - case 25: - switch (name[24]) { - case 's': - if (memeq("upgrade-insecure-request", name, 24)) { - return NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS; - } - break; - case 'y': - if (memeq("strict-transport-securit", name, 24)) { - return NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY; - } - break; - } - break; - case 27: - switch (name[26]) { - case 'n': - if (memeq("access-control-allow-origi", name, 26)) { - return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; - } - break; - } - break; - case 28: - switch (name[27]) { - case 's': - if (memeq("access-control-allow-header", name, 27)) { - return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS; - } - if (memeq("access-control-allow-method", name, 27)) { - return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS; - } - break; - } - break; - case 29: - switch (name[28]) { - case 'd': - if (memeq("access-control-request-metho", name, 28)) { - return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD; - } - break; - case 's': - if (memeq("access-control-expose-header", name, 28)) { - return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS; - } - break; - } - break; - case 30: - switch (name[29]) { - case 's': - if (memeq("access-control-request-header", name, 29)) { - return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS; - } - break; - } - break; - case 32: - switch (name[31]) { - case 's': - if (memeq("access-control-allow-credential", name, 31)) { - return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS; - } - break; - } - break; - } - return -1; -} - -static size_t table_space(size_t namelen, size_t valuelen) { - return NGHTTP3_QPACK_ENTRY_OVERHEAD + namelen + valuelen; -} - -static int qpack_nv_name_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { - return a->name->len == b->namelen && - memeq(a->name->base, b->name, b->namelen); -} - -static int qpack_nv_value_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { - return a->value->len == b->valuelen && - memeq(a->value->base, b->value, b->valuelen); -} - -static void qpack_map_init(nghttp3_qpack_map *map) { - memset(map, 0, sizeof(nghttp3_qpack_map)); -} - -static void qpack_map_insert(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { - nghttp3_qpack_entry **bucket; - - bucket = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; - - if (*bucket == NULL) { - *bucket = ent; - return; - } - - /* larger absidx is linked near the root */ - ent->map_next = *bucket; - *bucket = ent; -} - -static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { - nghttp3_qpack_entry **dst; - - dst = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; - - for (; *dst; dst = &(*dst)->map_next) { - if (*dst != ent) { - continue; - } - - *dst = ent->map_next; - ent->map_next = NULL; - return; - } -} - -/* - * qpack_context_can_reference returns nonzero if dynamic table entry - * at |absidx| can be referenced. In other words, it is within - * ctx->max_dtable_size. - */ -static int qpack_context_can_reference(nghttp3_qpack_context *ctx, - uint64_t absidx) { - nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); - return ctx->dtable_sum - ent->sum <= ctx->max_dtable_size; -} - -/* |*ppb_match| (post-base match), if it is not NULL, is always exact - match. */ -static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, - int *exact_match, - nghttp3_qpack_entry **pmatch, - nghttp3_qpack_entry **ppb_match, - const nghttp3_nv *nv, int32_t token, - uint32_t hash, uint64_t krcnt, - int allow_blocking, int name_only) { - nghttp3_qpack_entry *p; - - *exact_match = 0; - *pmatch = NULL; - *ppb_match = NULL; - - for (p = encoder->dtable_map.table[hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; p; - p = p->map_next) { - if (token != p->nv.token || - (token == -1 && (hash != p->hash || !qpack_nv_name_eq(&p->nv, nv))) || - !qpack_context_can_reference(&encoder->ctx, p->absidx)) { - continue; - } - if (allow_blocking || p->absidx + 1 <= krcnt) { - if (!*pmatch) { - *pmatch = p; - if (name_only) { - return; - } - } - if (qpack_nv_value_eq(&p->nv, nv)) { - *pmatch = p; - *exact_match = 1; - return; - } - } else if (!*ppb_match && qpack_nv_value_eq(&p->nv, nv)) { - *ppb_match = p; - } - } -} - -/* - * qpack_context_init initializes |ctx|. |max_dtable_size| is the - * maximum size of dynamic table. |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. - */ -static int qpack_context_init(nghttp3_qpack_context *ctx, - size_t max_dtable_size, size_t max_blocked, - 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; - } - - ctx->mem = mem; - ctx->dtable_size = 0; - ctx->dtable_sum = 0; - ctx->hard_max_dtable_size = max_dtable_size; - ctx->max_dtable_size = 0; - ctx->max_blocked = max_blocked; - ctx->next_absidx = 0; - ctx->bad = 0; - - return 0; -} - -static void qpack_context_free(nghttp3_qpack_context *ctx) { - nghttp3_qpack_entry *ent; - size_t i, len = nghttp3_ringbuf_len(&ctx->dtable); - - for (i = 0; i < len; ++i) { - ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i); - nghttp3_qpack_entry_free(ent); - nghttp3_mem_free(ctx->mem, ent); - } - nghttp3_ringbuf_free(&ctx->dtable); -} - -static int ref_min_cnt_less(const nghttp3_pq_entry *lhsx, - const nghttp3_pq_entry *rhsx) { - nghttp3_qpack_header_block_ref *lhs = - nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, min_cnts_pe); - nghttp3_qpack_header_block_ref *rhs = - nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, min_cnts_pe); - - return lhs->min_cnt < rhs->min_cnt; -} - -typedef struct { - uint64_t max_cnt; - uint64_t id; -} nghttp3_blocked_streams_key; - -static int max_cnt_greater(const nghttp3_ksl_key *lhs, - const nghttp3_ksl_key *rhs) { - const nghttp3_blocked_streams_key *a = lhs; - const nghttp3_blocked_streams_key *b = rhs; - 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 max_dtable_size, size_t max_blocked, - const nghttp3_mem *mem) { - int rv; - - rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem); - if (rv != 0) { - return rv; - } - - rv = nghttp3_map_init(&encoder->streams, mem); - if (rv != 0) { - goto streams_init_fail; - } - - rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, - sizeof(nghttp3_blocked_streams_key), mem); - if (rv != 0) { - goto blocked_streams_init_fail; - } - - qpack_map_init(&encoder->dtable_map); - nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem); - - encoder->krcnt = 0; - encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE; - encoder->opcode = 0; - encoder->min_dtable_update = SIZE_MAX; - encoder->last_max_dtable_update = 0; - encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE; - - nghttp3_qpack_read_state_reset(&encoder->rstate); - - return 0; - -blocked_streams_init_fail: - nghttp3_map_free(&encoder->streams); -streams_init_fail: - qpack_context_free(&encoder->ctx); - - return rv; -} - -static int map_stream_free(nghttp3_map_entry *entry, void *ptr) { - const nghttp3_mem *mem = ptr; - nghttp3_qpack_stream *stream = - nghttp3_struct_of(entry, nghttp3_qpack_stream, me); - nghttp3_qpack_stream_del(stream, mem); - return 0; -} - -void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) { - nghttp3_pq_free(&encoder->min_cnts); - nghttp3_ksl_free(&encoder->blocked_streams); - nghttp3_map_each_free(&encoder->streams, map_stream_free, - (void *)encoder->ctx.mem); - nghttp3_map_free(&encoder->streams); - qpack_context_free(&encoder->ctx); -} - -int nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size) { - if (encoder->ctx.hard_max_dtable_size < max_dtable_size) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (encoder->ctx.max_dtable_size == max_dtable_size) { - return 0; - } - - encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; - - if (encoder->min_dtable_update > max_dtable_size) { - encoder->min_dtable_update = max_dtable_size; - encoder->ctx.max_dtable_size = max_dtable_size; - } - encoder->last_max_dtable_update = max_dtable_size; - - return 0; -} - -int nghttp3_qpack_encoder_set_hard_max_dtable_size( - nghttp3_qpack_encoder *encoder, size_t hard_max_dtable_size) { - /* TODO This is not ideal. */ - if (encoder->ctx.hard_max_dtable_size) { - return NGHTTP3_ERR_INVALID_STATE; - } - - encoder->ctx.hard_max_dtable_size = hard_max_dtable_size; - - return 0; -} - -int nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, - size_t max_blocked) { - /* TODO This is not ideal. */ - if (encoder->ctx.max_blocked) { - return NGHTTP3_ERR_INVALID_STATE; - } - - encoder->ctx.max_blocked = max_blocked; - - return 0; -} - -uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) { - assert(!nghttp3_pq_empty(&encoder->min_cnts)); - - return nghttp3_struct_of(nghttp3_pq_top(&encoder->min_cnts), - nghttp3_qpack_header_block_ref, min_cnts_pe) - ->min_cnt; -} - -void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { - nghttp3_ringbuf *dtable = &encoder->ctx.dtable; - const nghttp3_mem *mem = encoder->ctx.mem; - uint64_t min_cnt = UINT64_MAX; - size_t len; - nghttp3_qpack_entry *ent; - - if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) { - return; - } - - if (!nghttp3_pq_empty(&encoder->min_cnts)) { - min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); - } - - for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) { - len = nghttp3_ringbuf_len(dtable); - ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); - if (ent->absidx + 1 == min_cnt) { - return; - } - - encoder->ctx.dtable_size -= - table_space(ent->nv.name->len, ent->nv.value->len); - - nghttp3_ringbuf_pop_back(dtable); - qpack_map_remove(&encoder->dtable_map, ent); - - nghttp3_qpack_entry_free(ent); - nghttp3_mem_free(mem, ent); - } -} - -/* - * qpack_encoder_add_stream_ref adds another dynamic table reference - * to a stream denoted by |stream_id|. |stream| must be NULL if no - * stream object is not found for the given stream ID. |max_cnt| and - * |min_cnt| is the maximum and minimum insert count it references - * respectively. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder, - int64_t stream_id, - nghttp3_qpack_stream *stream, - uint64_t max_cnt, uint64_t min_cnt) { - nghttp3_qpack_header_block_ref *ref; - const nghttp3_mem *mem = encoder->ctx.mem; - uint64_t prev_max_cnt = 0; - int rv; - - if (stream == NULL) { - rv = nghttp3_qpack_stream_new(&stream, stream_id, mem); - if (rv != 0) { - assert(rv == NGHTTP3_ERR_NOMEM); - return rv; - } - rv = nghttp3_map_insert(&encoder->streams, &stream->me); - if (rv != 0) { - assert(rv == NGHTTP3_ERR_NOMEM); - nghttp3_qpack_stream_del(stream, mem); - return rv; - } - } else { - prev_max_cnt = nghttp3_qpack_stream_get_max_cnt(stream); - if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream) && - max_cnt > prev_max_cnt) { - nghttp3_qpack_encoder_unblock_stream(encoder, stream); - } - } - - rv = nghttp3_qpack_header_block_ref_new(&ref, max_cnt, min_cnt, mem); - if (rv != 0) { - return rv; - } - - rv = nghttp3_qpack_stream_add_ref(stream, ref); - if (rv != 0) { - nghttp3_qpack_header_block_ref_del(ref, mem); - return rv; - } - - if (max_cnt > prev_max_cnt && - nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) { - rv = nghttp3_qpack_encoder_block_stream(encoder, stream); - if (rv != 0) { - return rv; - } - } - - return nghttp3_pq_push(&encoder->min_cnts, &ref->min_cnts_pe); -} - -static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder, - nghttp3_qpack_stream *stream) { - size_t i, len; - nghttp3_qpack_header_block_ref *ref; - - nghttp3_map_remove(&encoder->streams, stream->me.key); - - len = nghttp3_ringbuf_len(&stream->refs); - for (i = 0; i < len; ++i) { - ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, - i); - - assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); - - nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe); - } -} - -/* - * reserve_buf_internal ensures that |buf| contains at least - * |extra_size| of free space. In other words, if this function - * succeeds, nghttp2_buf_left(buf) >= extra_size holds. |min_size| is - * the minimum size of buffer. The allocated buffer has at least - * |min_size| bytes. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -static int reserve_buf_internal(nghttp3_buf *buf, size_t extra_size, - size_t min_size, const nghttp3_mem *mem) { - size_t left = nghttp3_buf_left(buf); - size_t n = min_size, need; - - if (left >= extra_size) { - return 0; - } - - need = nghttp3_buf_cap(buf) + extra_size - left; - - for (; n < need; n *= 2) - ; - - return nghttp3_buf_reserve(buf, n, mem); -} - -static int reserve_buf_small(nghttp3_buf *buf, size_t extra_size, - const nghttp3_mem *mem) { - return reserve_buf_internal(buf, extra_size, 32, mem); -} - -static int reserve_buf(nghttp3_buf *buf, size_t extra_size, - const nghttp3_mem *mem) { - return reserve_buf_internal(buf, extra_size, 32, mem); -} - -int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, - nghttp3_buf *pbuf, nghttp3_buf *rbuf, - nghttp3_buf *ebuf, int64_t stream_id, - const nghttp3_nv *nva, size_t nvlen) { - size_t i; - uint64_t max_cnt = 0, min_cnt = UINT64_MAX; - uint64_t base; - int rv = 0; - int allow_blocking; - int blocked_stream; - nghttp3_qpack_stream *stream; - - if (encoder->ctx.bad) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - rv = nghttp3_qpack_encoder_process_dtable_update(encoder, ebuf); - if (rv != 0) { - goto fail; - } - - base = encoder->ctx.next_absidx; - - stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id); - blocked_stream = - stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream); - allow_blocking = - blocked_stream || - encoder->ctx.max_blocked > nghttp3_ksl_len(&encoder->blocked_streams); - - DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id, - blocked_stream, allow_blocking); - - for (i = 0; i < nvlen; ++i) { - rv = nghttp3_qpack_encoder_encode_nv(encoder, &max_cnt, &min_cnt, rbuf, - ebuf, &nva[i], base, allow_blocking); - if (rv != 0) { - goto fail; - } - } - - nghttp3_qpack_encoder_write_field_section_prefix(encoder, pbuf, max_cnt, - base); - - /* TODO If max_cnt == 0, no reference is made to dtable. */ - if (!max_cnt) { - return 0; - } - - rv = qpack_encoder_add_stream_ref(encoder, stream_id, stream, max_cnt, - min_cnt); - if (rv != 0) { - goto fail; - } - - return 0; - -fail: - encoder->ctx.bad = 1; - return rv; -} - -/* - * qpack_write_number writes variable integer to |rbuf|. |num| is an - * integer to write. |prefix| is a prefix of variable integer - * encoding. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -static int qpack_write_number(nghttp3_buf *rbuf, uint8_t fb, uint64_t num, - size_t prefix, const nghttp3_mem *mem) { - int rv; - size_t len = nghttp3_qpack_put_varint_len(num, prefix); - uint8_t *p; - - rv = reserve_buf(rbuf, len, mem); - if (rv != 0) { - return rv; - } - - p = rbuf->last; - - *p = fb; - p = nghttp3_qpack_put_varint(p, num, prefix); - - assert((size_t)(p - rbuf->last) == len); - - rbuf->last = p; - - return 0; -} - -int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf) { - int rv; - - nghttp3_qpack_encoder_shrink_dtable(encoder); - - if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size || - !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) { - return 0; - } - - if (encoder->min_dtable_update < encoder->last_max_dtable_update) { - rv = nghttp3_qpack_encoder_write_set_dtable_cap(encoder, ebuf, - encoder->min_dtable_update); - if (rv != 0) { - return rv; - } - } - - rv = nghttp3_qpack_encoder_write_set_dtable_cap( - encoder, ebuf, encoder->last_max_dtable_update); - if (rv != 0) { - return rv; - } - - encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; - encoder->min_dtable_update = SIZE_MAX; - encoder->ctx.max_dtable_size = encoder->last_max_dtable_update; - - return 0; -} - -int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, size_t cap) { - DEBUGF("qpack::encode: Set Dynamic Table Capacity capacity=%zu\n", cap); - return qpack_write_number(ebuf, 0x20, cap, 5, encoder->ctx.mem); -} - -nghttp3_qpack_stream * -nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, - int64_t stream_id) { - nghttp3_map_entry *me = - nghttp3_map_find(&encoder->streams, (uint64_t)stream_id); - return me == NULL ? NULL : nghttp3_struct_of(me, nghttp3_qpack_stream, me); -} - -int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, - nghttp3_qpack_stream *stream) { - return stream && encoder->krcnt < nghttp3_qpack_stream_get_max_cnt(stream); -} - -/* - * qpack_encoder_decide_indexing_mode determines and returns indexing - * mode for header field |nv|. |token| is a token of header field - * name. - */ -static nghttp3_qpack_indexing_mode -qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder, - const nghttp3_nv *nv, int32_t token) { - if (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) { - return NGHTTP3_QPACK_INDEXING_MODE_NEVER; - } - - switch (token) { - case NGHTTP3_QPACK_TOKEN_AUTHORIZATION: - return NGHTTP3_QPACK_INDEXING_MODE_NEVER; - case NGHTTP3_QPACK_TOKEN_COOKIE: - if (nv->valuelen < 20) { - return NGHTTP3_QPACK_INDEXING_MODE_NEVER; - } - break; - case -1: - case NGHTTP3_QPACK_TOKEN__PATH: - case NGHTTP3_QPACK_TOKEN_AGE: - case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: - case NGHTTP3_QPACK_TOKEN_ETAG: - case NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE: - case NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH: - case NGHTTP3_QPACK_TOKEN_LOCATION: - case NGHTTP3_QPACK_TOKEN_SET_COOKIE: - return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; - case NGHTTP3_QPACK_TOKEN_HOST: - case NGHTTP3_QPACK_TOKEN_TE: - case NGHTTP3_QPACK_TOKEN__PROTOCOL: - case NGHTTP3_QPACK_TOKEN_PRIORITY: - break; - default: - if (token >= 1000) { - return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; - } - } - - if (table_space(nv->namelen, nv->valuelen) > - encoder->ctx.max_dtable_size * 3 / 4) { - return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; - } - - return NGHTTP3_QPACK_INDEXING_MODE_STORE; -} - -/* - * qpack_encoder_can_index returns nonzero if an entry which occupies - * |need| bytes can be inserted into dynamic table. |min_cnt| is the - * minimum insert count which blocked stream requires. - */ -static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, - uint64_t min_cnt) { - size_t avail = 0; - size_t len; - uint64_t gmin_cnt; - nghttp3_qpack_entry *min_ent, *last_ent; - nghttp3_ringbuf *dtable = &encoder->ctx.dtable; - - if (encoder->ctx.max_dtable_size > encoder->ctx.dtable_size) { - avail = encoder->ctx.max_dtable_size - encoder->ctx.dtable_size; - if (need <= avail) { - return 1; - } - } - - if (!nghttp3_pq_empty(&encoder->min_cnts)) { - gmin_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); - min_cnt = nghttp3_min(min_cnt, gmin_cnt); - } - - if (min_cnt == UINT64_MAX) { - return encoder->ctx.max_dtable_size >= need; - } - - min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1); - - len = nghttp3_ringbuf_len(&encoder->ctx.dtable); - assert(len); - last_ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); - - if (min_ent == last_ent) { - return 0; - } - - return avail + min_ent->sum - last_ent->sum >= need; -} - -/* - * qpack_encoder_can_index_nv returns nonzero if header field |nv| can - * be inserted into dynamic table. |min_cnt| is the minimum insert - * count which blocked stream requires. - */ -static int qpack_encoder_can_index_nv(nghttp3_qpack_encoder *encoder, - const nghttp3_nv *nv, uint64_t min_cnt) { - return qpack_encoder_can_index( - encoder, table_space(nv->namelen, nv->valuelen), min_cnt); -} - -/* - * qpack_encoder_can_index_duplicate returns nonzero if an entry at - * |absidx| in dynamic table can be inserted to dynamic table as - * duplicate. |min_cnt| is the minimum insert count which blocked - * stream requires. - */ -static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder, - uint64_t absidx, - uint64_t min_cnt) { - nghttp3_qpack_entry *ent = - nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); - - return qpack_encoder_can_index( - encoder, table_space(ent->nv.name->len, ent->nv.value->len), min_cnt); -} - -/* - * qpack_context_check_draining returns nonzero if an entry at - * |absidx| in dynamic table is one of draining entries. - */ -static int qpack_context_check_draining(nghttp3_qpack_context *ctx, - uint64_t absidx) { - const size_t safe = - ctx->max_dtable_size - nghttp3_min(512, ctx->max_dtable_size * 1 / 8); - nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); - - return ctx->dtable_sum - ent->sum > safe; -} - -int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, - uint64_t *pmax_cnt, uint64_t *pmin_cnt, - nghttp3_buf *rbuf, nghttp3_buf *ebuf, - const nghttp3_nv *nv, uint64_t base, - int allow_blocking) { - 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_entry *new_ent = NULL; - int static_entry; - int just_index = 0; - int rv; - - token = qpack_lookup_token(nv->name, nv->namelen); - static_entry = token != -1 && (size_t)token < nghttp3_arraylen(token_stable); - if (static_entry) { - hash = token_stable[token].hash; - } else { - switch (token) { - case NGHTTP3_QPACK_TOKEN_HOST: - hash = 2952701295u; - break; - case NGHTTP3_QPACK_TOKEN_TE: - hash = 1011170994u; - break; - case NGHTTP3_QPACK_TOKEN__PROTOCOL: - hash = 1128642621u; - break; - case NGHTTP3_QPACK_TOKEN_PRIORITY: - hash = 2498028297u; - break; - } - } - - indexing_mode = qpack_encoder_decide_indexing_mode(encoder, nv, token); - - if (static_entry) { - sres = nghttp3_qpack_lookup_stable(nv, token, indexing_mode); - if (sres.index != -1 && sres.name_value_match) { - return nghttp3_qpack_encoder_write_static_indexed(encoder, rbuf, - (size_t)sres.index); - } - } - - if (hash && - nghttp3_map_size(&encoder->streams) < NGHTTP3_QPACK_MAX_QPACK_STREAMS) { - dres = nghttp3_qpack_encoder_lookup_dtable(encoder, nv, token, hash, - indexing_mode, encoder->krcnt, - allow_blocking); - just_index = indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_STORE && - dres.pb_index == -1; - } - - if (dres.index != -1 && dres.name_value_match) { - if (allow_blocking && - qpack_context_check_draining(&encoder->ctx, (size_t)dres.index) && - qpack_encoder_can_index_duplicate(encoder, (size_t)dres.index, - *pmin_cnt)) { - rv = nghttp3_qpack_encoder_write_duplicate_insert(encoder, ebuf, - (size_t)dres.index); - if (rv != 0) { - return rv; - } - rv = nghttp3_qpack_encoder_dtable_duplicate_add(encoder, - (size_t)dres.index); - if (rv != 0) { - return rv; - } - - new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); - dres.index = (nghttp3_ssize)new_ent->absidx; - } - *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1)); - *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1)); - - return nghttp3_qpack_encoder_write_dynamic_indexed( - encoder, rbuf, (size_t)dres.index, base); - } - - if (sres.index != -1) { - if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) { - rv = nghttp3_qpack_encoder_write_static_insert(encoder, ebuf, - (size_t)sres.index, nv); - if (rv != 0) { - return rv; - } - rv = nghttp3_qpack_encoder_dtable_static_add(encoder, (size_t)sres.index, - nv, hash); - if (rv != 0) { - return rv; - } - if (allow_blocking) { - new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); - *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); - *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); - - return nghttp3_qpack_encoder_write_dynamic_indexed( - encoder, rbuf, new_ent->absidx, base); - } - } - - return nghttp3_qpack_encoder_write_static_indexed_name( - encoder, rbuf, (size_t)sres.index, nv); - } - - if (dres.index != -1) { - if (just_index && - qpack_encoder_can_index_nv( - encoder, nv, - allow_blocking ? *pmin_cnt - : nghttp3_min((size_t)dres.index + 1, *pmin_cnt))) { - rv = nghttp3_qpack_encoder_write_dynamic_insert(encoder, ebuf, - (size_t)dres.index, nv); - if (rv != 0) { - return rv; - } - - if (!allow_blocking) { - *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)dres.index + 1); - } - - rv = nghttp3_qpack_encoder_dtable_dynamic_add(encoder, (size_t)dres.index, - nv, hash); - if (rv != 0) { - return rv; - } - - if (allow_blocking) { - new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); - *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); - *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); - - return nghttp3_qpack_encoder_write_dynamic_indexed( - encoder, rbuf, new_ent->absidx, base); - } - } - - *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1)); - *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1)); - - return nghttp3_qpack_encoder_write_dynamic_indexed_name( - encoder, rbuf, (size_t)dres.index, base, nv); - } - - if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) { - rv = nghttp3_qpack_encoder_dtable_literal_add(encoder, nv, token, hash); - if (rv != 0) { - return rv; - } - rv = nghttp3_qpack_encoder_write_literal_insert(encoder, ebuf, nv); - if (rv != 0) { - return rv; - } - if (allow_blocking) { - new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); - *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); - *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); - - return nghttp3_qpack_encoder_write_dynamic_indexed(encoder, rbuf, - new_ent->absidx, base); - } - } - - return nghttp3_qpack_encoder_write_literal(encoder, rbuf, nv); -} - -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_static_entry *ent; - nghttp3_qpack_static_header *hdr; - size_t i; - - assert(token >= 0); - - if (indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER) { - return res; - } - - for (i = (size_t)token; - i < nghttp3_arraylen(token_stable) && token_stable[i].token == token; - ++i) { - ent = &token_stable[i]; - hdr = &stable[ent->absidx]; - if (hdr->value.len == nv->valuelen && - memeq(hdr->value.base, nv->value, nv->valuelen)) { - res.index = (nghttp3_ssize)ent->absidx; - res.name_value_match = 1; - return res; - } - } - return res; -} - -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}; - int exact_match = 0; - nghttp3_qpack_entry *match, *pb_match; - - encoder_qpack_map_find(encoder, &exact_match, &match, &pb_match, nv, token, - hash, krcnt, allow_blocking, - indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER); - if (match) { - res.index = (nghttp3_ssize)match->absidx; - res.name_value_match = exact_match; - } - if (pb_match) { - res.pb_index = (nghttp3_ssize)pb_match->absidx; - } - - return res; -} - -int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, - uint64_t max_cnt, uint64_t min_cnt, - const nghttp3_mem *mem) { - nghttp3_qpack_header_block_ref *ref = - nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_header_block_ref)); - - if (ref == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - ref->max_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; - ref->min_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; - ref->max_cnt = max_cnt; - ref->min_cnt = min_cnt; - - *pref = ref; - - return 0; -} - -void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, - const nghttp3_mem *mem) { - nghttp3_mem_free(mem, ref); -} - -static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx, - const nghttp3_pq_entry *rhsx) { - const nghttp3_qpack_header_block_ref *lhs = - nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); - const nghttp3_qpack_header_block_ref *rhs = - nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); - - return lhs->max_cnt > rhs->max_cnt; -} - -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)); - if (stream == NULL) { - 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_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); - - stream->me.next = NULL; - stream->me.key = (uint64_t)stream_id; - - *pstream = stream; - - return 0; -} - -void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, - const nghttp3_mem *mem) { - nghttp3_qpack_header_block_ref *ref; - size_t i, len; - - if (stream == NULL) { - return; - } - - nghttp3_pq_free(&stream->max_cnts); - - len = nghttp3_ringbuf_len(&stream->refs); - for (i = 0; i < len; ++i) { - ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, - i); - nghttp3_qpack_header_block_ref_del(ref, mem); - } - - nghttp3_ringbuf_free(&stream->refs); - - nghttp3_mem_free(mem, stream); -} - -uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream) { - nghttp3_qpack_header_block_ref *ref; - - if (nghttp3_pq_empty(&stream->max_cnts)) { - return 0; - } - - ref = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), - nghttp3_qpack_header_block_ref, max_cnts_pe); - return ref->max_cnt; -} - -int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, - nghttp3_qpack_header_block_ref *ref) { - nghttp3_qpack_header_block_ref **dest; - int rv; - - if (nghttp3_ringbuf_full(&stream->refs)) { - rv = nghttp3_ringbuf_reserve(&stream->refs, - nghttp3_ringbuf_len(&stream->refs) * 2); - 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); -} - -void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) { - nghttp3_qpack_header_block_ref *ref; - - assert(nghttp3_ringbuf_len(&stream->refs)); - - ref = - *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); - - assert(ref->max_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); - - nghttp3_pq_remove(&stream->max_cnts, &ref->max_cnts_pe); - - nghttp3_ringbuf_pop_front(&stream->refs); -} - -int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, - nghttp3_buf *rbuf, - uint64_t absidx) { - DEBUGF("qpack::encode: Indexed Field Line (static) absidx=%" PRIu64 "\n", - absidx); - return qpack_write_number(rbuf, 0xc0, absidx, 6, encoder->ctx.mem); -} - -int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, - nghttp3_buf *rbuf, - uint64_t absidx, - uint64_t base) { - DEBUGF("qpack::encode: Indexed Field Line (dynamic) absidx=%" PRIu64 - " base=%" PRIu64 "\n", - absidx, base); - - if (absidx < base) { - return qpack_write_number(rbuf, 0x80, base - absidx - 1, 6, - encoder->ctx.mem); - } - - return qpack_write_number(rbuf, 0x10, absidx - base, 4, encoder->ctx.mem); -} - -/* - * qpack_encoder_write_indexed_name writes generic indexed name. |fb| - * is the first byte. |nameidx| is an index of referenced name. - * |prefix| is a prefix of variable integer encoding. |nv| is a - * header field to encode. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder, - nghttp3_buf *buf, uint8_t fb, - uint64_t nameidx, size_t prefix, - const nghttp3_nv *nv) { - int rv; - size_t len = nghttp3_qpack_put_varint_len(nameidx, prefix); - uint8_t *p; - size_t hlen; - int h = 0; - - hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); - if (hlen < nv->valuelen) { - h = 1; - len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen; - } else { - len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; - } - - rv = reserve_buf(buf, len, encoder->ctx.mem); - if (rv != 0) { - return rv; - } - - p = buf->last; - - *p = fb; - p = nghttp3_qpack_put_varint(p, nameidx, prefix); - - if (h) { - *p = 0x80; - p = nghttp3_qpack_put_varint(p, hlen, 7); - p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); - } else { - *p = 0; - p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); - if (nv->valuelen) { - p = nghttp3_cpymem(p, nv->value, nv->valuelen); - } - } - - assert((size_t)(p - buf->last) == len); - - buf->last = p; - - return 0; -} - -int nghttp3_qpack_encoder_write_static_indexed_name( - nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, - const nghttp3_nv *nv) { - uint8_t fb = - (uint8_t)(0x50 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); - - DEBUGF("qpack::encode: Literal Field Line With Name Reference (static) " - "absidx=%" PRIu64 " never=%d\n", - absidx, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); - return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx, 4, nv); -} - -int nghttp3_qpack_encoder_write_dynamic_indexed_name( - nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, - uint64_t base, const nghttp3_nv *nv) { - uint8_t fb; - - DEBUGF("qpack::encode: Literal Field Line With Name Reference (dynamic) " - "absidx=%" PRIu64 " base=%" PRIu64 " never=%d\n", - absidx, base, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); - - if (absidx < base) { - fb = (uint8_t)(0x40 | - ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); - return qpack_encoder_write_indexed_name(encoder, rbuf, fb, - base - absidx - 1, 4, nv); - } - - fb = (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x08 : 0; - return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx - base, 3, - nv); -} - -/* - * qpack_encoder_write_literal writes generic literal header field - * representation. |fb| is a first byte. |prefix| is a prefix of - * variable integer encoding for name length. |nv| is a header field - * to encode. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, - nghttp3_buf *buf, uint8_t fb, - size_t prefix, const nghttp3_nv *nv) { - int rv; - size_t len; - uint8_t *p; - size_t nhlen, vhlen; - int nh = 0, vh = 0; - - nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen); - if (nhlen < nv->namelen) { - nh = 1; - len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen; - } else { - len = nghttp3_qpack_put_varint_len(nv->namelen, prefix) + nv->namelen; - } - - vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); - if (vhlen < nv->valuelen) { - vh = 1; - len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen; - } else { - len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; - } - - rv = reserve_buf(buf, len, encoder->ctx.mem); - if (rv != 0) { - return rv; - } - - p = buf->last; - - *p = fb; - if (nh) { - *p |= (uint8_t)(1 << prefix); - p = nghttp3_qpack_put_varint(p, nhlen, prefix); - p = nghttp3_qpack_huffman_encode(p, nv->name, nv->namelen); - } else { - p = nghttp3_qpack_put_varint(p, nv->namelen, prefix); - if (nv->namelen) { - p = nghttp3_cpymem(p, nv->name, nv->namelen); - } - } - - *p = 0; - - if (vh) { - *p |= 0x80; - p = nghttp3_qpack_put_varint(p, vhlen, 7); - p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); - } else { - p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); - if (nv->valuelen) { - p = nghttp3_cpymem(p, nv->value, nv->valuelen); - } - } - - assert((size_t)(p - buf->last) == len); - - buf->last = p; - - return 0; -} - -int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, - nghttp3_buf *rbuf, - const nghttp3_nv *nv) { - uint8_t fb = - (uint8_t)(0x20 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x10 : 0)); - - DEBUGF("qpack::encode: Literal Field Line Without Name Reference\n"); - return qpack_encoder_write_literal(encoder, rbuf, fb, 3, nv); -} - -int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - uint64_t absidx, - const nghttp3_nv *nv) { - DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%" PRIu64 - "\n", - absidx); - return qpack_encoder_write_indexed_name(encoder, ebuf, 0xc0, absidx, 6, nv); -} - -int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - uint64_t absidx, - const nghttp3_nv *nv) { - DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%" PRIu64 - "\n", - absidx); - return qpack_encoder_write_indexed_name( - encoder, ebuf, 0x80, encoder->ctx.next_absidx - absidx - 1, 6, nv); -} - -int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - uint64_t absidx) { - uint64_t idx = encoder->ctx.next_absidx - absidx - 1; - size_t len = nghttp3_qpack_put_varint_len(idx, 5); - uint8_t *p; - int rv; - - DEBUGF("qpack::encode: Insert duplicate absidx=%" PRIu64 "\n", absidx); - - rv = reserve_buf(ebuf, len, encoder->ctx.mem); - if (rv != 0) { - return rv; - } - - p = ebuf->last; - - *p = 0; - p = nghttp3_qpack_put_varint(p, idx, 5); - - assert((size_t)(p - ebuf->last) == len); - - ebuf->last = p; - - return 0; -} - -int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - const nghttp3_nv *nv) { - DEBUGF("qpack::encode: Insert Without Name Reference\n"); - return qpack_encoder_write_literal(encoder, ebuf, 0x40, 5, nv); -} - -int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, - nghttp3_qpack_nv *qnv, - nghttp3_qpack_map *dtable_map, - uint32_t hash) { - nghttp3_qpack_entry *new_ent, **p, *ent; - const nghttp3_mem *mem = ctx->mem; - size_t space; - size_t i; - int rv; - - space = table_space(qnv->name->len, qnv->value->len); - - assert(space <= ctx->max_dtable_size); - - while (ctx->dtable_size + space > ctx->max_dtable_size) { - i = nghttp3_ringbuf_len(&ctx->dtable); - assert(i); - ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); - - ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len); - - nghttp3_ringbuf_pop_back(&ctx->dtable); - if (dtable_map) { - qpack_map_remove(dtable_map, ent); - } - - nghttp3_qpack_entry_free(ent); - nghttp3_mem_free(mem, ent); - } - - new_ent = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_entry)); - if (new_ent == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - nghttp3_qpack_entry_init(new_ent, qnv, ctx->dtable_sum, ctx->next_absidx++, - hash); - - if (nghttp3_ringbuf_full(&ctx->dtable)) { - rv = nghttp3_ringbuf_reserve(&ctx->dtable, - nghttp3_ringbuf_len(&ctx->dtable) * 2); - if (rv != 0) { - goto fail; - } - } - - p = nghttp3_ringbuf_push_front(&ctx->dtable); - *p = new_ent; - - if (dtable_map) { - qpack_map_insert(dtable_map, new_ent); - } - - ctx->dtable_size += space; - ctx->dtable_sum += space; - - return 0; - -fail: - nghttp3_qpack_entry_free(new_ent); - nghttp3_mem_free(mem, new_ent); - - return rv; -} - -int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, - uint64_t absidx, - const nghttp3_nv *nv, - uint32_t hash) { - const nghttp3_qpack_static_header *shd; - nghttp3_qpack_nv qnv; - const nghttp3_mem *mem = encoder->ctx.mem; - int rv; - - rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); - if (rv != 0) { - return rv; - } - - assert(nghttp3_arraylen(stable) > absidx); - - shd = &stable[absidx]; - - qnv.name = (nghttp3_rcbuf *)&shd->name; - qnv.token = shd->token; - qnv.flags = NGHTTP3_NV_FLAG_NONE; - - rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, - &encoder->dtable_map, hash); - - nghttp3_rcbuf_decref(qnv.value); - - return rv; -} - -int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, - uint64_t absidx, - const nghttp3_nv *nv, - uint32_t hash) { - nghttp3_qpack_nv qnv; - nghttp3_qpack_entry *ent; - const nghttp3_mem *mem = encoder->ctx.mem; - int rv; - - rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); - if (rv != 0) { - return rv; - } - - ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); - - qnv.name = ent->nv.name; - qnv.token = ent->nv.token; - qnv.flags = NGHTTP3_NV_FLAG_NONE; - - nghttp3_rcbuf_incref(qnv.name); - - rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, - &encoder->dtable_map, hash); - - nghttp3_rcbuf_decref(qnv.value); - nghttp3_rcbuf_decref(qnv.name); - - return rv; -} - -int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, - uint64_t absidx) { - nghttp3_qpack_nv qnv; - nghttp3_qpack_entry *ent; - int rv; - - ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); - - qnv = ent->nv; - nghttp3_rcbuf_incref(qnv.name); - nghttp3_rcbuf_incref(qnv.value); - - rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, - &encoder->dtable_map, ent->hash); - - nghttp3_rcbuf_decref(qnv.name); - nghttp3_rcbuf_decref(qnv.value); - - return rv; -} - -int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, - const nghttp3_nv *nv, - int32_t token, uint32_t hash) { - nghttp3_qpack_nv qnv; - const nghttp3_mem *mem = encoder->ctx.mem; - int rv; - - rv = nghttp3_rcbuf_new2(&qnv.name, nv->name, nv->namelen, mem); - if (rv != 0) { - return rv; - } - - rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); - if (rv != 0) { - nghttp3_rcbuf_decref(qnv.name); - return rv; - } - - qnv.token = token; - qnv.flags = NGHTTP3_NV_FLAG_NONE; - - rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, - &encoder->dtable_map, hash); - - nghttp3_rcbuf_decref(qnv.value); - nghttp3_rcbuf_decref(qnv.name); - - return rv; -} - -nghttp3_qpack_entry * -nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx) { - size_t relidx; - - assert(ctx->next_absidx > absidx); - assert(ctx->next_absidx - absidx - 1 < nghttp3_ringbuf_len(&ctx->dtable)); - - relidx = (size_t)(ctx->next_absidx - absidx - 1); - - return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, relidx); -} - -nghttp3_qpack_entry * -nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx) { - assert(nghttp3_ringbuf_len(&ctx->dtable)); - return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, 0); -} - -void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, - size_t sum, uint64_t absidx, uint32_t hash) { - ent->nv = *qnv; - ent->map_next = NULL; - ent->sum = sum; - ent->absidx = absidx; - ent->hash = hash; - - nghttp3_rcbuf_incref(ent->nv.name); - nghttp3_rcbuf_incref(ent->nv.value); -} - -void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) { - nghttp3_rcbuf_decref(ent->nv.value); - nghttp3_rcbuf_decref(ent->nv.name); -} - -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, - stream->me.key}; - - return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream); -} - -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, - stream->me.key}; - nghttp3_ksl_it it; - - /* This is purely debugging purpose only */ - it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); - - assert(!nghttp3_ksl_it_end(&it)); - assert(nghttp3_ksl_it_get(&it) == stream); - - nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &bsk); -} - -void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, - uint64_t max_cnt) { - nghttp3_blocked_streams_key bsk = {max_cnt, 0}; - nghttp3_ksl_it it; - - it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); - - for (; !nghttp3_ksl_it_end(&it);) { - bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it); - nghttp3_ksl_remove(&encoder->blocked_streams, &it, &bsk); - } -} - -void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, - int64_t stream_id) { - nghttp3_qpack_stream *stream = - nghttp3_qpack_encoder_find_stream(encoder, stream_id); - const nghttp3_mem *mem = encoder->ctx.mem; - nghttp3_qpack_header_block_ref *ref; - - if (stream == NULL) { - /* This might be NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR, but we - don't create stream which does not use dynamic table. */ - return; - } - - assert(nghttp3_ringbuf_len(&stream->refs)); - - ref = - *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); - - DEBUGF("qpack::encoder: Header acknowledgement stream=%ld ricnt=%" PRIu64 - " krcnt=%" PRIu64 "\n", - stream_id, ref->max_cnt, encoder->krcnt); - - if (encoder->krcnt < ref->max_cnt) { - encoder->krcnt = ref->max_cnt; - - nghttp3_qpack_encoder_unblock(encoder, ref->max_cnt); - } - - nghttp3_qpack_stream_pop_ref(stream); - - assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); - - nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe); - - nghttp3_qpack_header_block_ref_del(ref, mem); - - if (nghttp3_ringbuf_len(&stream->refs)) { - return; - } - - qpack_encoder_remove_stream(encoder, stream); - - nghttp3_qpack_stream_del(stream, mem); -} - -int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, - uint64_t n) { - if (encoder->ctx.next_absidx - encoder->krcnt < n) { - return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; - } - encoder->krcnt += n; - - nghttp3_qpack_encoder_unblock(encoder, encoder->krcnt); - - return 0; -} - -void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) { - encoder->krcnt = encoder->ctx.next_absidx; - - nghttp3_ksl_clear(&encoder->blocked_streams); - nghttp3_pq_clear(&encoder->min_cnts); - nghttp3_map_each_free(&encoder->streams, map_stream_free, - (void *)encoder->ctx.mem); -} - -void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, - int64_t stream_id) { - nghttp3_qpack_stream *stream = - nghttp3_qpack_encoder_find_stream(encoder, stream_id); - const nghttp3_mem *mem = encoder->ctx.mem; - - if (stream == NULL) { - return; - } - - if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) { - nghttp3_qpack_encoder_unblock_stream(encoder, stream); - } - - qpack_encoder_remove_stream(encoder, stream); - - nghttp3_qpack_stream_del(stream, mem); -} - -size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) { - return nghttp3_ksl_len(&encoder->blocked_streams); -} - -int nghttp3_qpack_encoder_write_field_section_prefix( - nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, - uint64_t base) { - size_t max_ents = - encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; - uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1; - int sign = base < ricnt; - uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt; - size_t len = nghttp3_qpack_put_varint_len(encricnt, 8) + - nghttp3_qpack_put_varint_len(delta_base, 7); - uint8_t *p; - int rv; - - DEBUGF("qpack::encode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n", - ricnt, base, encoder->ctx.next_absidx); - - rv = reserve_buf(pbuf, len, encoder->ctx.mem); - if (rv != 0) { - return rv; - } - - p = pbuf->last; - - p = nghttp3_qpack_put_varint(p, encricnt, 8); - if (sign) { - *p = 0x80; - } else { - *p = 0; - } - p = nghttp3_qpack_put_varint(p, delta_base, 7); - - assert((size_t)(p - pbuf->last) == len); - - pbuf->last = p; - - return 0; -} - -/* - * qpack_read_varint reads |rstate->prefix| prefixed integer stored - * from |begin|. The |end| represents the 1 beyond the last of the - * valid contiguous memory region from |begin|. The decoded integer - * must be less than or equal to NGHTTP3_QPACK_INT_MAX. - * - * If the |rstate->left| is nonzero, it is used as an initial value, - * and this function assumes the |begin| starts with intermediate - * data. |rstate->shift| is used as initial integer shift. - * - * If an entire integer is decoded successfully, the |*fin| is set to - * nonzero. - * - * This function stores the decoded integer in |rstate->left| if it - * succeeds, including partial decoding (in this case, number of shift - * to make in the next call will be stored in |rstate->shift|) and - * returns number of bytes processed, or returns negative error code - * NGHTTP3_ERR_QPACK_FATAL, indicating decoding error. - */ -static nghttp3_ssize qpack_read_varint(int *fin, - nghttp3_qpack_read_state *rstate, - const uint8_t *begin, - const uint8_t *end) { - uint64_t k = (uint8_t)((1 << rstate->prefix) - 1); - uint64_t n = rstate->left; - uint64_t add; - const uint8_t *p = begin; - size_t shift = rstate->shift; - - rstate->shift = 0; - *fin = 0; - - if (n == 0) { - if (((*p) & k) != k) { - rstate->left = (*p) & k; - *fin = 1; - return 1; - } - - n = k; - - if (++p == end) { - rstate->left = n; - return (nghttp3_ssize)(p - begin); - } - } - - for (; p != end; ++p, shift += 7) { - add = (*p) & 0x7f; - - if (shift > 62) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - if ((NGHTTP3_QPACK_INT_MAX >> shift) < add) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - add <<= shift; - - if (NGHTTP3_QPACK_INT_MAX - add < n) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - n += add; - - if (((*p) & (1 << 7)) == 0) { - break; - } - } - - rstate->shift = shift; - - if (p == end) { - rstate->left = n; - return (nghttp3_ssize)(p - begin); - } - - rstate->left = n; - *fin = 1; - return (nghttp3_ssize)(p + 1 - begin); -} - -nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder, - const uint8_t *src, - size_t srclen) { - const uint8_t *p = src, *end; - int rv; - nghttp3_ssize nread; - int rfin; - - if (encoder->ctx.bad) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - if (srclen == 0) { - return 0; - } - - end = src + srclen; - - for (; p != end;) { - switch (encoder->state) { - case NGHTTP3_QPACK_DS_STATE_OPCODE: - if ((*p) & 0x80) { - DEBUGF("qpack::encode: OPCODE_SECTION_ACK\n"); - encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK; - encoder->rstate.prefix = 7; - } else if ((*p) & 0x40) { - DEBUGF("qpack::encode: OPCODE_STREAM_CANCEL\n"); - encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL; - encoder->rstate.prefix = 6; - } else { - DEBUGF("qpack::encode: OPCODE_ICNT_INCREMENT\n"); - encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT; - encoder->rstate.prefix = 6; - } - encoder->state = NGHTTP3_QPACK_DS_STATE_READ_NUMBER; - /* fall through */ - case NGHTTP3_QPACK_DS_STATE_READ_NUMBER: - nread = qpack_read_varint(&rfin, &encoder->rstate, p, end); - if (nread < 0) { - assert(nread == NGHTTP3_ERR_QPACK_FATAL); - rv = NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; - goto fail; - } - - p += nread; - - if (!rfin) { - return p - src; - } - - switch (encoder->opcode) { - case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT: - rv = nghttp3_qpack_encoder_add_insert_count(encoder, - encoder->rstate.left); - if (rv != 0) { - goto fail; - } - break; - case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK: - nghttp3_qpack_encoder_ack_header(encoder, - (int64_t)encoder->rstate.left); - break; - case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL: - nghttp3_qpack_encoder_cancel_stream(encoder, - (int64_t)encoder->rstate.left); - break; - default: - /* unreachable */ - assert(0); - break; - } - - encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&encoder->rstate); - break; - default: - /* unreachable */ - assert(0); - break; - } - } - - return p - src; - -fail: - encoder->ctx.bad = 1; - return rv; -} - -size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix) { - size_t k = (size_t)((1 << prefix) - 1); - size_t len = 0; - - if (n < k) { - return 1; - } - - n -= k; - ++len; - - for (; n >= 128; n >>= 7, ++len) - ; - - return len + 1; -} - -uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix) { - size_t k = (size_t)((1 << prefix) - 1); - - *buf = (uint8_t)(*buf & ~k); - - if (n < k) { - *buf = (uint8_t)(*buf | n); - return buf + 1; - } - - *buf = (uint8_t)(*buf | k); - ++buf; - - n -= k; - - for (; n >= 128; n >>= 7) { - *buf++ = (uint8_t)((1 << 7) | (n & 0x7f)); - } - - *buf++ = (uint8_t)n; - - return buf; -} - -void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate) { - nghttp3_rcbuf_decref(rstate->value); - nghttp3_rcbuf_decref(rstate->name); -} - -void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) { - rstate->name = NULL; - rstate->value = NULL; - nghttp3_buf_init(&rstate->namebuf); - nghttp3_buf_init(&rstate->valuebuf); - rstate->left = 0; - rstate->prefix = 0; - rstate->shift = 0; - rstate->absidx = 0; - rstate->never = 0; - rstate->dynamic = 0; - rstate->huffman_encoded = 0; -} - -int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t max_dtable_size, size_t max_blocked, - const nghttp3_mem *mem) { - int rv; - - rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem); - if (rv != 0) { - return rv; - } - - decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; - decoder->opcode = 0; - decoder->written_icnt = 0; - decoder->max_concurrent_streams = 0; - - nghttp3_qpack_read_state_reset(&decoder->rstate); - nghttp3_buf_init(&decoder->dbuf); - - return 0; -} - -void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) { - nghttp3_buf_free(&decoder->dbuf, decoder->ctx.mem); - nghttp3_qpack_read_state_free(&decoder->rstate); - qpack_context_free(&decoder->ctx); -} - -/* - * qpack_read_huffman_string decodes huffman string in buffer [begin, - * end) and writes the decoded string to |dest|. This function - * assumes the buffer pointed by |dest| has enough space. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_QPACK_FATAL - * Could not decode huffman string. - */ -static nghttp3_ssize qpack_read_huffman_string(nghttp3_qpack_read_state *rstate, - nghttp3_buf *dest, - const uint8_t *begin, - const uint8_t *end) { - nghttp3_ssize nwrite; - size_t len = (size_t)(end - begin); - int fin = 0; - - if (len >= rstate->left) { - len = (size_t)rstate->left; - fin = 1; - } - - nwrite = nghttp3_qpack_huffman_decode(&rstate->huffman_ctx, dest->last, begin, - len, fin); - if (nwrite < 0) { - return nwrite; - } - - if (nghttp3_qpack_huffman_decode_failure_state(&rstate->huffman_ctx)) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - dest->last += nwrite; - rstate->left -= len; - return (nghttp3_ssize)len; -} - -static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate, - nghttp3_buf *dest, const uint8_t *begin, - const uint8_t *end) { - size_t len = (size_t)(end - begin); - size_t n = (size_t)nghttp3_min((uint64_t)len, rstate->left); - - dest->last = nghttp3_cpymem(dest->last, begin, n); - - rstate->left -= n; - return (nghttp3_ssize)n; -} - -/* - * qpack_decoder_validate_index checks rstate->absidx is acceptable. - * - * It returns 0 if it suceeds, or one of the following negative error - * codes: - * - * NGHTTP3_ERR_QPACK_FATAL - * rstate->absidx is invalid. - */ -static int qpack_decoder_validate_index(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_read_state *rstate) { - if (rstate->dynamic) { - return rstate->absidx < decoder->ctx.next_absidx && - decoder->ctx.next_absidx - rstate->absidx - 1 < - nghttp3_ringbuf_len(&decoder->ctx.dtable) - ? 0 - : NGHTTP3_ERR_QPACK_FATAL; - } - return rstate->absidx < nghttp3_arraylen(stable) ? 0 - : NGHTTP3_ERR_QPACK_FATAL; -} - -static void qpack_read_state_check_huffman(nghttp3_qpack_read_state *rstate, - const uint8_t b) { - rstate->huffman_encoded = (b & (1 << rstate->prefix)) != 0; -} - -static void qpack_read_state_terminate_name(nghttp3_qpack_read_state *rstate) { - *rstate->namebuf.last = '\0'; - rstate->name->len = nghttp3_buf_len(&rstate->namebuf); -} - -static void qpack_read_state_terminate_value(nghttp3_qpack_read_state *rstate) { - *rstate->valuebuf.last = '\0'; - rstate->value->len = nghttp3_buf_len(&rstate->valuebuf); -} - -nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, - const uint8_t *src, - size_t srclen) { - const uint8_t *p = src, *end; - int rv; - int busy = 0; - const nghttp3_mem *mem = decoder->ctx.mem; - nghttp3_ssize nread; - int rfin; - - if (decoder->ctx.bad) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - if (srclen == 0) { - return 0; - } - - end = src + srclen; - - for (; p != end || busy;) { - busy = 0; - switch (decoder->state) { - case NGHTTP3_QPACK_ES_STATE_OPCODE: - if ((*p) & 0x80) { - DEBUGF("qpack::decode: OPCODE_INSERT_INDEXED\n"); - decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED; - decoder->rstate.dynamic = !((*p) & 0x40); - decoder->rstate.prefix = 6; - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; - } else if ((*p) & 0x40) { - DEBUGF("qpack::decode: OPCODE_INSERT\n"); - decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT; - decoder->rstate.dynamic = 0; - decoder->rstate.prefix = 5; - decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN; - } else if ((*p) & 0x20) { - DEBUGF("qpack::decode: OPCODE_SET_DTABLE_TABLE_CAP\n"); - decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP; - decoder->rstate.prefix = 5; - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; - } else if (!((*p) & 0x20)) { - DEBUGF("qpack::decode: OPCODE_DUPLICATE\n"); - decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_DUPLICATE; - decoder->rstate.dynamic = 1; - decoder->rstate.prefix = 5; - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; - } else { - DEBUGF("qpack::decode: unknown opcode %02x\n", *p); - rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - goto fail; - } - break; - case NGHTTP3_QPACK_ES_STATE_READ_INDEX: - nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - goto fail; - } - - p += nread; - - if (!rfin) { - return p - src; - } - - if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) { - if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) { - rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - goto fail; - } - DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n", - decoder->rstate.left); - nghttp3_qpack_decoder_set_dtable_cap(decoder, - (size_t)decoder->rstate.left); - - decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&decoder->rstate); - break; - } - - rv = nghttp3_qpack_decoder_rel2abs(decoder, &decoder->rstate); - if (rv < 0) { - goto fail; - } - - if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_DUPLICATE) { - rv = nghttp3_qpack_decoder_dtable_duplicate_add(decoder); - if (rv != 0) { - goto fail; - } - decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&decoder->rstate); - break; - } - - if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED) { - decoder->rstate.prefix = 7; - decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; - break; - } - - /* Unreachable */ - assert(0); - break; - case NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN: - qpack_read_state_check_huffman(&decoder->rstate, *p); - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAMELEN; - decoder->rstate.left = 0; - decoder->rstate.shift = 0; - /* Fall through */ - case NGHTTP3_QPACK_ES_STATE_READ_NAMELEN: - nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - goto fail; - } - - p += nread; - - if (!rfin) { - return p - src; - } - - if (decoder->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) { - rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; - goto fail; - } - - if (decoder->rstate.huffman_encoded) { - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN; - nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); - rv = nghttp3_rcbuf_new(&decoder->rstate.name, - (size_t)decoder->rstate.left * 2 + 1, mem); - } else { - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME; - rv = nghttp3_rcbuf_new(&decoder->rstate.name, - (size_t)decoder->rstate.left + 1, mem); - } - if (rv != 0) { - goto fail; - } - - nghttp3_buf_wrap_init(&decoder->rstate.namebuf, - decoder->rstate.name->base, - decoder->rstate.name->len); - break; - case NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN: - nread = qpack_read_huffman_string(&decoder->rstate, - &decoder->rstate.namebuf, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - goto fail; - } - - p += nread; - - if (decoder->rstate.left) { - return p - src; - } - - qpack_read_state_terminate_name(&decoder->rstate); - - decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; - decoder->rstate.prefix = 7; - break; - case NGHTTP3_QPACK_ES_STATE_READ_NAME: - nread = - qpack_read_string(&decoder->rstate, &decoder->rstate.namebuf, p, end); - if (nread < 0) { - rv = (int)nread; - goto fail; - } - - p += nread; - - if (decoder->rstate.left) { - return p - src; - } - - qpack_read_state_terminate_name(&decoder->rstate); - - decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; - decoder->rstate.prefix = 7; - break; - case NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN: - qpack_read_state_check_huffman(&decoder->rstate, *p); - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUELEN; - decoder->rstate.left = 0; - decoder->rstate.shift = 0; - /* Fall through */ - case NGHTTP3_QPACK_ES_STATE_READ_VALUELEN: - nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - goto fail; - } - - p += nread; - - if (!rfin) { - return p - src; - } - - if (decoder->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) { - rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; - goto fail; - } - - if (decoder->rstate.huffman_encoded) { - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN; - nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); - rv = nghttp3_rcbuf_new(&decoder->rstate.value, - (size_t)decoder->rstate.left * 2 + 1, mem); - } else { - decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE; - rv = nghttp3_rcbuf_new(&decoder->rstate.value, - (size_t)decoder->rstate.left + 1, mem); - } - if (rv != 0) { - goto fail; - } - - nghttp3_buf_wrap_init(&decoder->rstate.valuebuf, - decoder->rstate.value->base, - decoder->rstate.value->len); - - /* value might be 0 length */ - busy = 1; - break; - case NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN: - nread = qpack_read_huffman_string(&decoder->rstate, - &decoder->rstate.valuebuf, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - goto fail; - } - - p += nread; - - if (decoder->rstate.left) { - return p - src; - } - - qpack_read_state_terminate_value(&decoder->rstate); - - switch (decoder->opcode) { - case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: - rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder); - break; - case NGHTTP3_QPACK_ES_OPCODE_INSERT: - rv = nghttp3_qpack_decoder_dtable_literal_add(decoder); - break; - default: - /* Unreachable */ - assert(0); - } - if (rv != 0) { - goto fail; - } - - decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&decoder->rstate); - break; - case NGHTTP3_QPACK_ES_STATE_READ_VALUE: - nread = qpack_read_string(&decoder->rstate, &decoder->rstate.valuebuf, p, - end); - if (nread < 0) { - rv = (int)nread; - goto fail; - } - - p += nread; - - if (decoder->rstate.left) { - return p - src; - } - - qpack_read_state_terminate_value(&decoder->rstate); - - switch (decoder->opcode) { - case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: - rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder); - break; - case NGHTTP3_QPACK_ES_OPCODE_INSERT: - rv = nghttp3_qpack_decoder_dtable_literal_add(decoder); - break; - default: - /* Unreachable */ - assert(0); - } - if (rv != 0) { - goto fail; - } - - decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&decoder->rstate); - break; - } - } - - return p - src; - -fail: - decoder->ctx.bad = 1; - return rv; -} - -void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, - size_t cap) { - nghttp3_qpack_entry *ent; - size_t i; - nghttp3_qpack_context *ctx = &decoder->ctx; - const nghttp3_mem *mem = ctx->mem; - - ctx->max_dtable_size = cap; - - while (ctx->dtable_size > cap) { - i = nghttp3_ringbuf_len(&ctx->dtable); - assert(i); - ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); - - ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len); - - nghttp3_ringbuf_pop_back(&ctx->dtable); - nghttp3_qpack_entry_free(ent); - nghttp3_mem_free(mem, ent); - } -} - -int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) { - DEBUGF("qpack::decode: Insert With Name Reference (%s) absidx=%" PRIu64 ": " - "value=%*s\n", - decoder->rstate.dynamic ? "dynamic" : "static", decoder->rstate.absidx, - (int)decoder->rstate.value->len, decoder->rstate.value->base); - - if (decoder->rstate.dynamic) { - return nghttp3_qpack_decoder_dtable_dynamic_add(decoder); - } - - return nghttp3_qpack_decoder_dtable_static_add(decoder); -} - -int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { - nghttp3_qpack_nv qnv; - int rv; - const nghttp3_qpack_static_header *shd; - - shd = &stable[decoder->rstate.absidx]; - - if (table_space(shd->name.len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { - return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - } - - qnv.name = (nghttp3_rcbuf *)&shd->name; - qnv.value = decoder->rstate.value; - qnv.token = shd->token; - qnv.flags = NGHTTP3_NV_FLAG_NONE; - - rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); - - nghttp3_rcbuf_decref(qnv.value); - - return rv; -} - -int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) { - nghttp3_qpack_nv qnv; - int rv; - nghttp3_qpack_entry *ent; - - ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); - - if (table_space(ent->nv.name->len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { - return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - } - - qnv.name = ent->nv.name; - qnv.value = decoder->rstate.value; - qnv.token = ent->nv.token; - qnv.flags = NGHTTP3_NV_FLAG_NONE; - - nghttp3_rcbuf_incref(qnv.name); - - rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); - - nghttp3_rcbuf_decref(qnv.value); - nghttp3_rcbuf_decref(qnv.name); - - return rv; -} - -int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) { - int rv; - nghttp3_qpack_entry *ent; - nghttp3_qpack_nv qnv; - - DEBUGF("qpack::decode: Insert duplicate absidx=%" PRIu64 "\n", - decoder->rstate.absidx); - - ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); - - if (table_space(ent->nv.name->len, ent->nv.value->len) > - decoder->ctx.max_dtable_size) { - return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - } - - qnv = ent->nv; - nghttp3_rcbuf_incref(qnv.name); - nghttp3_rcbuf_incref(qnv.value); - - rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); - - nghttp3_rcbuf_decref(qnv.value); - nghttp3_rcbuf_decref(qnv.name); - - return rv; -} - -int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) { - nghttp3_qpack_nv qnv; - int rv; - - DEBUGF("qpack::decode: Insert Without Name Reference: name=%*s value=%*s\n", - (int)decoder->rstate.name->len, decoder->rstate.name->base, - (int)decoder->rstate.value->len, decoder->rstate.value->base); - - if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { - return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - } - - qnv.name = decoder->rstate.name; - qnv.value = decoder->rstate.value; - qnv.token = qpack_lookup_token(qnv.name->base, qnv.name->len); - qnv.flags = NGHTTP3_NV_FLAG_NONE; - - rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); - - nghttp3_rcbuf_decref(qnv.value); - nghttp3_rcbuf_decref(qnv.name); - - return rv; -} - -void nghttp3_qpack_decoder_set_max_concurrent_streams( - nghttp3_qpack_decoder *decoder, size_t max_concurrent_streams) { - decoder->max_concurrent_streams = - nghttp3_max(decoder->max_concurrent_streams, max_concurrent_streams); -} - -void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx, - int64_t stream_id, - const nghttp3_mem *mem) { - nghttp3_qpack_read_state_reset(&sctx->rstate); - - sctx->mem = mem; - sctx->rstate.prefix = 8; - sctx->state = NGHTTP3_QPACK_RS_STATE_RICNT; - sctx->opcode = 0; - sctx->stream_id = stream_id; - sctx->ricnt = 0; - sctx->dbase_sign = 0; - sctx->base = 0; -} - -void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx) { - nghttp3_qpack_read_state_free(&sctx->rstate); -} - -void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx) { - nghttp3_qpack_stream_context_init(sctx, sctx->stream_id, sctx->mem); -} - -uint64_t -nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx) { - return sctx->ricnt; -} - -nghttp3_ssize -nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv, uint8_t *pflags, - const uint8_t *src, size_t srclen, int fin) { - const uint8_t *p = src, *end = src ? src + srclen : src; - int rv; - int busy = 0; - nghttp3_ssize nread; - int rfin; - const nghttp3_mem *mem = decoder->ctx.mem; - - if (decoder->ctx.bad) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - *pflags = NGHTTP3_QPACK_DECODE_FLAG_NONE; - - for (; p != end || busy;) { - busy = 0; - switch (sctx->state) { - case NGHTTP3_QPACK_RS_STATE_RICNT: - nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - p += nread; - - if (!rfin) { - goto almost_ok; - } - - rv = nghttp3_qpack_decoder_reconstruct_ricnt(decoder, &sctx->ricnt, - sctx->rstate.left); - if (rv != 0) { - goto fail; - } - - sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE_SIGN; - break; - case NGHTTP3_QPACK_RS_STATE_DBASE_SIGN: - if ((*p) & 0x80) { - sctx->dbase_sign = 1; - } - sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE; - sctx->rstate.left = 0; - sctx->rstate.prefix = 7; - sctx->rstate.shift = 0; - /* Fall through */ - case NGHTTP3_QPACK_RS_STATE_DBASE: - nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - p += nread; - - if (!rfin) { - goto almost_ok; - } - - if (sctx->dbase_sign) { - if (sctx->ricnt < sctx->rstate.left) { - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - sctx->base = sctx->ricnt - sctx->rstate.left - 1; - } else { - sctx->base = sctx->ricnt + sctx->rstate.left; - } - - DEBUGF("qpack::decode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 - "\n", - sctx->ricnt, sctx->base, decoder->ctx.next_absidx); - - if (sctx->ricnt > decoder->ctx.next_absidx) { - DEBUGF("qpack::decode: stream blocked\n"); - sctx->state = NGHTTP3_QPACK_RS_STATE_BLOCKED; - *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; - return p - src; - } - - sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; - sctx->rstate.left = 0; - sctx->rstate.shift = 0; - break; - case NGHTTP3_QPACK_RS_STATE_OPCODE: - assert(sctx->rstate.left == 0); - assert(sctx->rstate.shift == 0); - if ((*p) & 0x80) { - DEBUGF("qpack::decode: OPCODE_INDEXED\n"); - sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED; - sctx->rstate.dynamic = !((*p) & 0x40); - sctx->rstate.prefix = 6; - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; - } else if ((*p) & 0x40) { - DEBUGF("qpack::decode: OPCODE_INDEXED_NAME\n"); - sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME; - sctx->rstate.never = (*p) & 0x20; - sctx->rstate.dynamic = !((*p) & 0x10); - sctx->rstate.prefix = 4; - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; - } else if ((*p) & 0x20) { - DEBUGF("qpack::decode: OPCODE_LITERAL\n"); - sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_LITERAL; - sctx->rstate.never = (*p) & 0x10; - sctx->rstate.dynamic = 0; - sctx->rstate.prefix = 3; - sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN; - } else if ((*p) & 0x10) { - DEBUGF("qpack::decode: OPCODE_INDEXED_PB\n"); - sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB; - sctx->rstate.dynamic = 1; - sctx->rstate.prefix = 4; - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; - } else { - DEBUGF("qpack::decode: OPCODE_INDEXED_NAME_PB\n"); - sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB; - sctx->rstate.never = (*p) & 0x08; - sctx->rstate.dynamic = 1; - sctx->rstate.prefix = 3; - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; - } - break; - case NGHTTP3_QPACK_RS_STATE_READ_INDEX: - nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - p += nread; - - if (!rfin) { - goto almost_ok; - } - - switch (sctx->opcode) { - case NGHTTP3_QPACK_RS_OPCODE_INDEXED: - rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx); - if (rv != 0) { - goto fail; - } - nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv); - *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; - - sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&sctx->rstate); - - return p - src; - case NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB: - rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx); - if (rv != 0) { - goto fail; - } - nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv); - *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; - - sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&sctx->rstate); - - return p - src; - case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: - rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx); - if (rv != 0) { - goto fail; - } - sctx->rstate.prefix = 7; - sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; - break; - case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: - rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx); - if (rv != 0) { - goto fail; - } - sctx->rstate.prefix = 7; - sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; - break; - default: - /* Unreachable */ - assert(0); - } - break; - case NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN: - qpack_read_state_check_huffman(&sctx->rstate, *p); - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAMELEN; - sctx->rstate.left = 0; - sctx->rstate.shift = 0; - /* Fall through */ - case NGHTTP3_QPACK_RS_STATE_READ_NAMELEN: - nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - p += nread; - - if (!rfin) { - goto almost_ok; - } - - if (sctx->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) { - rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; - goto fail; - } - - if (sctx->rstate.huffman_encoded) { - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN; - nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); - rv = nghttp3_rcbuf_new(&sctx->rstate.name, - (size_t)sctx->rstate.left * 2 + 1, mem); - } else { - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME; - rv = nghttp3_rcbuf_new(&sctx->rstate.name, - (size_t)sctx->rstate.left + 1, mem); - } - if (rv != 0) { - goto fail; - } - - nghttp3_buf_wrap_init(&sctx->rstate.namebuf, sctx->rstate.name->base, - sctx->rstate.name->len); - break; - case NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN: - nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.namebuf, p, - end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - p += nread; - - if (sctx->rstate.left) { - goto almost_ok; - } - - qpack_read_state_terminate_name(&sctx->rstate); - - sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; - sctx->rstate.prefix = 7; - break; - case NGHTTP3_QPACK_RS_STATE_READ_NAME: - nread = qpack_read_string(&sctx->rstate, &sctx->rstate.namebuf, p, end); - if (nread < 0) { - rv = (int)nread; - goto fail; - } - - p += nread; - - if (sctx->rstate.left) { - goto almost_ok; - } - - qpack_read_state_terminate_name(&sctx->rstate); - - sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; - sctx->rstate.prefix = 7; - break; - case NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN: - qpack_read_state_check_huffman(&sctx->rstate, *p); - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUELEN; - sctx->rstate.left = 0; - sctx->rstate.shift = 0; - /* Fall through */ - case NGHTTP3_QPACK_RS_STATE_READ_VALUELEN: - nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - p += nread; - - if (!rfin) { - goto almost_ok; - } - - if (sctx->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) { - rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; - goto fail; - } - - if (sctx->rstate.huffman_encoded) { - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN; - nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); - rv = nghttp3_rcbuf_new(&sctx->rstate.value, - (size_t)sctx->rstate.left * 2 + 1, mem); - } else { - sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE; - rv = nghttp3_rcbuf_new(&sctx->rstate.value, - (size_t)sctx->rstate.left + 1, mem); - } - if (rv != 0) { - goto fail; - } - - nghttp3_buf_wrap_init(&sctx->rstate.valuebuf, sctx->rstate.value->base, - sctx->rstate.value->len); - - /* value might be 0 length */ - busy = 1; - break; - case NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN: - nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.valuebuf, - p, end); - if (nread < 0) { - assert(NGHTTP3_ERR_QPACK_FATAL == nread); - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - p += nread; - - if (sctx->rstate.left) { - goto almost_ok; - } - - qpack_read_state_terminate_value(&sctx->rstate); - - switch (sctx->opcode) { - case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: - case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: - nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); - break; - case NGHTTP3_QPACK_RS_OPCODE_LITERAL: - nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); - break; - default: - /* Unreachable */ - assert(0); - } - - *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; - - sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&sctx->rstate); - - return p - src; - case NGHTTP3_QPACK_RS_STATE_READ_VALUE: - nread = qpack_read_string(&sctx->rstate, &sctx->rstate.valuebuf, p, end); - if (nread < 0) { - rv = (int)nread; - goto fail; - } - - p += nread; - - if (sctx->rstate.left) { - goto almost_ok; - } - - qpack_read_state_terminate_value(&sctx->rstate); - - switch (sctx->opcode) { - case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: - case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: - nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); - break; - case NGHTTP3_QPACK_RS_OPCODE_LITERAL: - nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); - break; - default: - /* Unreachable */ - assert(0); - } - - *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; - - sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&sctx->rstate); - - return p - src; - case NGHTTP3_QPACK_RS_STATE_BLOCKED: - if (sctx->ricnt > decoder->ctx.next_absidx) { - DEBUGF("qpack::decode: stream still blocked\n"); - *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; - return p - src; - } - sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; - nghttp3_qpack_read_state_reset(&sctx->rstate); - break; - } - } - -almost_ok: - if (fin) { - if (sctx->state != NGHTTP3_QPACK_RS_STATE_OPCODE) { - rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - goto fail; - } - - *pflags |= NGHTTP3_QPACK_DECODE_FLAG_FINAL; - - if (sctx->ricnt) { - rv = nghttp3_qpack_decoder_write_section_ack(decoder, sctx); - if (rv != 0) { - goto fail; - } - } - } - - return p - src; - -fail: - decoder->ctx.bad = 1; - return rv; -} - -static int qpack_decoder_dbuf_overflow(nghttp3_qpack_decoder *decoder) { - size_t limit = nghttp3_max(decoder->max_concurrent_streams, 100); - /* 10 = nghttp3_qpack_put_varint_len((1ULL << 62) - 1, 2)) */ - return nghttp3_buf_len(&decoder->dbuf) > limit * 2 * 10; -} - -int nghttp3_qpack_decoder_write_section_ack( - nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx) { - nghttp3_buf *dbuf = &decoder->dbuf; - uint8_t *p; - int rv; - - if (qpack_decoder_dbuf_overflow(decoder)) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - rv = reserve_buf_small( - dbuf, nghttp3_qpack_put_varint_len((uint64_t)sctx->stream_id, 7), - decoder->ctx.mem); - if (rv != 0) { - return rv; - } - - p = dbuf->last; - *p = 0x80; - dbuf->last = nghttp3_qpack_put_varint(p, (uint64_t)sctx->stream_id, 7); - - if (decoder->written_icnt < sctx->ricnt) { - decoder->written_icnt = sctx->ricnt; - } - - return 0; -} - -size_t -nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder) { - uint64_t n; - size_t len = 0; - - if (decoder->written_icnt < decoder->ctx.next_absidx) { - n = decoder->ctx.next_absidx - decoder->written_icnt; - len = nghttp3_qpack_put_varint_len(n, 6); - } - - return nghttp3_buf_len(&decoder->dbuf) + len; -} - -void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, - nghttp3_buf *dbuf) { - uint8_t *p; - uint64_t n = 0; - size_t len = 0; - - if (decoder->written_icnt < decoder->ctx.next_absidx) { - n = decoder->ctx.next_absidx - decoder->written_icnt; - len = nghttp3_qpack_put_varint_len(n, 6); - } - - assert(nghttp3_buf_left(dbuf) >= nghttp3_buf_len(&decoder->dbuf) + len); - - if (nghttp3_buf_len(&decoder->dbuf)) { - dbuf->last = nghttp3_cpymem(dbuf->last, decoder->dbuf.pos, - nghttp3_buf_len(&decoder->dbuf)); - } - - if (n) { - p = dbuf->last; - *p = 0; - dbuf->last = nghttp3_qpack_put_varint(p, n, 6); - - decoder->written_icnt = decoder->ctx.next_absidx; - } - - nghttp3_buf_reset(&decoder->dbuf); -} - -int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, - int64_t stream_id) { - uint8_t *p; - int rv; - - if (qpack_decoder_dbuf_overflow(decoder)) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - rv = reserve_buf(&decoder->dbuf, - nghttp3_qpack_put_varint_len((uint64_t)stream_id, 6), - decoder->ctx.mem); - if (rv != 0) { - return rv; - } - - p = decoder->dbuf.last; - *p = 0x40; - decoder->dbuf.last = nghttp3_qpack_put_varint(p, (uint64_t)stream_id, 6); - - return 0; -} - -int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, - uint64_t *dest, uint64_t encricnt) { - uint64_t max_ents, full, max, max_wrapped, ricnt; - - if (encricnt == 0) { - *dest = 0; - return 0; - } - - max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; - full = 2 * max_ents; - - if (encricnt > full) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - - max = decoder->ctx.next_absidx + max_ents; - max_wrapped = max / full * full; - ricnt = max_wrapped + encricnt - 1; - - if (ricnt > max) { - if (ricnt <= full) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - ricnt -= full; - } - - if (ricnt == 0) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - - *dest = ricnt; - - return 0; -} - -int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_read_state *rstate) { - DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " icnt=%" PRIu64 "\n", - rstate->dynamic, rstate->left, decoder->ctx.next_absidx); - - if (rstate->dynamic) { - if (decoder->ctx.next_absidx < rstate->left + 1) { - return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - } - rstate->absidx = decoder->ctx.next_absidx - rstate->left - 1; - } else { - rstate->absidx = rstate->left; - } - if (qpack_decoder_validate_index(decoder, rstate) != 0) { - return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; - } - return 0; -} - -int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx) { - nghttp3_qpack_read_state *rstate = &sctx->rstate; - - DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " base=%" PRIu64 - " icnt=%" PRIu64 "\n", - rstate->dynamic, rstate->left, sctx->base, decoder->ctx.next_absidx); - - if (rstate->dynamic) { - if (sctx->base < rstate->left + 1) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - rstate->absidx = sctx->base - rstate->left - 1; - - if (rstate->absidx >= sctx->ricnt) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - } else { - rstate->absidx = rstate->left; - } - - if (qpack_decoder_validate_index(decoder, rstate) != 0) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - return 0; -} - -int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx) { - nghttp3_qpack_read_state *rstate = &sctx->rstate; - - DEBUGF("qpack::decode: pbidx=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n", - rstate->left, sctx->base, decoder->ctx.next_absidx); - - assert(rstate->dynamic); - - rstate->absidx = rstate->left + sctx->base; - - if (rstate->absidx >= sctx->ricnt) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - - if (qpack_decoder_validate_index(decoder, rstate) != 0) { - return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; - } - return 0; -} - -static void -qpack_decoder_emit_static_indexed(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { - const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; - (void)decoder; - - nv->name = (nghttp3_rcbuf *)&shd->name; - nv->value = (nghttp3_rcbuf *)&shd->value; - nv->token = shd->token; - nv->flags = NGHTTP3_NV_FLAG_NONE; -} - -static void -qpack_decoder_emit_dynamic_indexed(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { - nghttp3_qpack_entry *ent = - nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); - - *nv = ent->nv; - - nghttp3_rcbuf_incref(nv->name); - nghttp3_rcbuf_incref(nv->value); -} - -void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { - DEBUGF("qpack::decode: Indexed (%s) absidx=%" PRIu64 "\n", - sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx); - - if (sctx->rstate.dynamic) { - qpack_decoder_emit_dynamic_indexed(decoder, sctx, nv); - } else { - qpack_decoder_emit_static_indexed(decoder, sctx, nv); - } -} - -static void -qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { - const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; - (void)decoder; - - nv->name = (nghttp3_rcbuf *)&shd->name; - nv->value = sctx->rstate.value; - nv->token = shd->token; - nv->flags = - sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; - - sctx->rstate.value = NULL; -} - -static void -qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { - nghttp3_qpack_entry *ent = - nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); - (void)decoder; - - nv->name = ent->nv.name; - nv->value = sctx->rstate.value; - nv->token = ent->nv.token; - nv->flags = - sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; - - nghttp3_rcbuf_incref(nv->name); - - sctx->rstate.value = NULL; -} - -void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { - (void)decoder; - - DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n", - sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx, - (int)sctx->rstate.value->len, sctx->rstate.value->base); - - if (sctx->rstate.dynamic) { - qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); - } else { - qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); - } -} - -void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { - (void)decoder; - - DEBUGF("qpack::decode: Emit literal name=%*s value=%*s\n", - (int)sctx->rstate.name->len, sctx->rstate.name->base, - (int)sctx->rstate.value->len, sctx->rstate.value->base); - - nv->name = sctx->rstate.name; - nv->value = sctx->rstate.value; - nv->token = qpack_lookup_token(nv->name->base, nv->name->len); - nv->flags = - sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; - - sctx->rstate.name = NULL; - sctx->rstate.value = NULL; -} - -int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t max_dtable_size, size_t max_blocked, - const nghttp3_mem *mem) { - int rv; - nghttp3_qpack_encoder *p; - - p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder)); - if (p == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem); - if (rv != 0) { - return rv; - } - - *pencoder = p; - - return 0; -} - -void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) { - const nghttp3_mem *mem; - - if (encoder == NULL) { - return; - } - - mem = encoder->ctx.mem; - - nghttp3_qpack_encoder_free(encoder); - nghttp3_mem_free(mem, encoder); -} - -int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, - int64_t stream_id, - const nghttp3_mem *mem) { - nghttp3_qpack_stream_context *p; - - p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context)); - if (p == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - nghttp3_qpack_stream_context_init(p, stream_id, mem); - - *psctx = p; - - return 0; -} - -void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) { - const nghttp3_mem *mem; - - if (sctx == NULL) { - return; - } - - mem = sctx->mem; - - nghttp3_qpack_stream_context_free(sctx); - nghttp3_mem_free(mem, sctx); -} - -int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, - size_t max_dtable_size, size_t max_blocked, - const nghttp3_mem *mem) { - int rv; - nghttp3_qpack_decoder *p; - - p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder)); - if (p == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem); - if (rv != 0) { - return rv; - } - - *pdecoder = p; - - return 0; -} - -void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder) { - const nghttp3_mem *mem; - - if (decoder == NULL) { - return; - } - - mem = decoder->ctx.mem; - - nghttp3_qpack_decoder_free(decoder); - nghttp3_mem_free(mem, decoder); -} - -uint64_t nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder) { - return decoder->ctx.next_absidx; -} diff --git a/deps/nghttp3/lib/nghttp3_qpack.h b/deps/nghttp3/lib/nghttp3_qpack.h deleted file mode 100644 index ddfb2b66385..00000000000 --- a/deps/nghttp3/lib/nghttp3_qpack.h +++ /dev/null @@ -1,966 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2013 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. - */ -#ifndef NGHTTP3_QPACK_H -#define NGHTTP3_QPACK_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_rcbuf.h" -#include "nghttp3_map.h" -#include "nghttp3_pq.h" -#include "nghttp3_ringbuf.h" -#include "nghttp3_buf.h" -#include "nghttp3_ksl.h" -#include "nghttp3_qpack_huffman.h" - -#define NGHTTP3_QPACK_INT_MAX ((1ull << 62) - 1) - -/* NGHTTP3_QPACK_MAX_NAMELEN is the maximum (compressed) length of - header name this library can decode. */ -#define NGHTTP3_QPACK_MAX_NAMELEN 256 -/* NGHTTP3_QPACK_MAX_VALUELEN is the maximum (compressed) length of - header value this library can decode. */ -#define NGHTTP3_QPACK_MAX_VALUELEN 65536 - -/* nghttp3_qpack_indexing_mode is a indexing strategy. */ -typedef enum { - /* NGHTTP3_QPACK_INDEXING_MODE_LITERAL means that header field - should not be inserted into dynamic table. */ - NGHTTP3_QPACK_INDEXING_MODE_LITERAL, - /* NGHTTP3_QPACK_INDEXING_MODE_STORE means that header field can be - inserted into dynamic table. */ - NGHTTP3_QPACK_INDEXING_MODE_STORE, - /* NGHTTP3_QPACK_INDEXING_MODE_NEVER means that header field should - not be inserted into dynamic table and this must be true for all - forwarding paths. */ - NGHTTP3_QPACK_INDEXING_MODE_NEVER, -} nghttp3_qpack_indexing_mode; - -struct nghttp3_qpack_entry; -typedef struct nghttp3_qpack_entry nghttp3_qpack_entry; - -struct nghttp3_qpack_entry { - /* The header field name/value pair */ - nghttp3_qpack_nv nv; - /* map_next points to the entry which shares same bucket in hash - table. */ - nghttp3_qpack_entry *map_next; - /* sum is the sum of all entries inserted up to this entry. This - value does not contain the space required for this entry. */ - size_t sum; - /* absidx is the absolute index of this entry. */ - uint64_t absidx; - /* The hash value for header name (nv.name). */ - uint32_t hash; -}; - -/* The entry used for static table. */ -typedef struct { - uint64_t absidx; - int32_t token; - uint32_t hash; -} nghttp3_qpack_static_entry; - -typedef struct { - nghttp3_rcbuf name; - nghttp3_rcbuf value; - int32_t token; -} nghttp3_qpack_static_header; - -/* - * nghttp3_qpack_header_block_ref is created per encoded header block - * and includes the required insert count and the minimum insert count - * of dynamic table entry it refers to. - */ -typedef struct { - nghttp3_pq_entry max_cnts_pe; - nghttp3_pq_entry min_cnts_pe; - /* max_cnt is the required insert count. */ - uint64_t max_cnt; - /* min_cnt is the minimum insert count of dynamic table entry it - refers to. In other words, this is the minimum absolute index of - dynamic header table entry this encoded block refers to plus - 1. */ - uint64_t min_cnt; -} nghttp3_qpack_header_block_ref; - -int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, - uint64_t max_cnt, uint64_t min_cnt, - const nghttp3_mem *mem); - -void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, - const nghttp3_mem *mem); - -typedef struct { - nghttp3_map_entry me; - /* refs is an array of pointer to nghttp3_qpack_header_block_ref in - the order of the time they are encoded. HTTP/3 allows multiple - header blocks (e.g., non-final response headers, final response - headers, trailers, and push promises) per stream. */ - nghttp3_ringbuf refs; - /* max_cnts is a priority queue sorted by descending order of - max_cnt of nghttp3_qpack_header_block_ref. */ - nghttp3_pq max_cnts; -} nghttp3_qpack_stream; - -int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, - const nghttp3_mem *mem); - -void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, - const nghttp3_mem *mem); - -uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream); - -int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, - nghttp3_qpack_header_block_ref *ref); - -void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream); - -#define NGHTTP3_QPACK_ENTRY_OVERHEAD 32 - -typedef struct { - /* dtable is a dynamic table */ - nghttp3_ringbuf dtable; - /* mem is memory allocator */ - const nghttp3_mem *mem; - /* dtable_size is abstracted buffer size of dtable as described in - the spec. This is the sum of length of name/value in dtable + - NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */ - size_t dtable_size; - size_t dtable_sum; - /* hard_max_dtable_size is the maximum size of dynamic table. In - HTTP/3, it is notified by decoder as - SETTINGS_QPACK_MAX_TABLE_CAPACITY. Any value lower than or equal - to SETTINGS_QPACK_MAX_TABLE_CAPACITY is OK because encoder has - the authority to decide how many entries are inserted into - dynamic table. */ - size_t hard_max_dtable_size; - /* max_dtable_size is the effective maximum size of dynamic table. */ - size_t max_dtable_size; - /* max_blocked is the maximum number of stream which can be - blocked. */ - size_t max_blocked; - /* next_absidx is the next absolute index for nghttp3_qpack_entry. - It is equivalent to insert count. */ - uint64_t next_absidx; - /* If inflate/deflate error occurred, this value is set to 1 and - further invocation of inflate/deflate will fail with - NGHTTP3_ERR_QPACK_FATAL. */ - uint8_t bad; -} nghttp3_qpack_context; - -typedef struct { - nghttp3_qpack_huffman_decode_context huffman_ctx; - nghttp3_buf namebuf; - nghttp3_buf valuebuf; - nghttp3_rcbuf *name; - nghttp3_rcbuf *value; - uint64_t left; - size_t prefix; - size_t shift; - uint64_t absidx; - int never; - int dynamic; - int huffman_encoded; -} nghttp3_qpack_read_state; - -void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate); - -void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate); - -#define NGHTTP3_QPACK_MAP_SIZE 64 - -typedef struct { - nghttp3_qpack_entry *table[NGHTTP3_QPACK_MAP_SIZE]; -} nghttp3_qpack_map; - -/* nghttp3_qpack_decoder_stream_state is a set of states when decoding - decoder stream. */ -typedef enum { - NGHTTP3_QPACK_DS_STATE_OPCODE, - NGHTTP3_QPACK_DS_STATE_READ_NUMBER, -} nghttp3_qpack_decoder_stream_state; - -/* nghttp3_qpack_decoder_stream_opcode is opcode used in decoder - stream. */ -typedef enum { - NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT, - NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK, - NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL, -} nghttp3_qpack_decoder_stream_opcode; - -/* nghttp3_qpack_encoder_flag is a set of flags used by - nghttp3_qpack_encoder. */ -typedef enum { - NGHTTP3_QPACK_ENCODER_FLAG_NONE = 0x00, - /* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that - Set Dynamic Table Capacity is required. */ - NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP = 0x01, -} nghttp3_qpack_encoder_flag; - -struct nghttp3_qpack_encoder { - nghttp3_qpack_context ctx; - /* dtable_map is a map of hash to nghttp3_qpack_entry to provide - fast access to an entry in dynamic table. */ - nghttp3_qpack_map dtable_map; - /* streams is a map of stream ID to nghttp3_qpack_stream to keep - track of unacknowledged streams. */ - nghttp3_map streams; - /* blocked_streams is an ordered list of nghttp3_qpack_stream, in - descending order of max_cnt, to search the unblocked streams by - received known count. */ - nghttp3_ksl blocked_streams; - /* min_cnts is a priority queue of nghttp3_qpack_header_block_ref - sorted by ascending order of min_cnt to know that an entry can be - evicted from dynamic table. */ - nghttp3_pq min_cnts; - /* krcnt is Known Received Count. */ - uint64_t krcnt; - /* state is a current state of reading decoder stream. */ - nghttp3_qpack_decoder_stream_state state; - /* opcode is a decoder stream opcode being processed. */ - nghttp3_qpack_decoder_stream_opcode opcode; - /* rstate is a set of intermediate state which are used to process - decoder stream. */ - nghttp3_qpack_read_state rstate; - /* min_dtable_update is the minimum dynamic table size required. */ - size_t min_dtable_update; - /* last_max_dtable_update is the dynamic table size last - requested. */ - size_t last_max_dtable_update; - /* flags is bitwise OR of zero or more of - nghttp3_qpack_encoder_flag. */ - uint8_t flags; -}; - -/* - * nghttp3_qpack_encoder_init initializes |encoder|. - * |max_dtable_size| is the maximum size of dynamic table. - * |max_blocked| 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_encoder_init(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size, size_t max_blocked, - const nghttp3_mem *mem); - -/* - * nghttp3_qpack_encoder_free frees memory allocated for |encoder|. - * This function does not free memory pointed by |encoder|. - */ -void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder); - -/* - * nghttp3_qpack_encoder_encode_nv encodes |nv|. It writes request - * stream into |rbuf| and writes encoder stream into |ebuf|. |nv| is - * a header field to encode. |base| is base. |allow_blocking| is - * nonzero if this stream can be blocked (or it has been blocked - * already). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, - uint64_t *pmax_cnt, uint64_t *pmin_cnt, - nghttp3_buf *rbuf, nghttp3_buf *ebuf, - const nghttp3_nv *nv, uint64_t base, - int allow_blocking); - -/* nghttp3_qpack_lookup_result stores a result of table lookup. */ -typedef struct { - /* index is an index of matched entry. -1 if no match is made. */ - nghttp3_ssize index; - /* name_value_match is nonzero if both name and value are - matched. */ - int name_value_match; - /* pb_index is the absolute index of matched post-based dynamic - table entry. -1 if no such entry exists. */ - nghttp3_ssize pb_index; -} nghttp3_qpack_lookup_result; - -/* - * nghttp3_qpack_lookup_stable searches |nv| in static table. |token| - * is a token of nv->name and it is -1 if there is no corresponding - * token defined. |indexing_mode| provides indexing strategy. - */ -nghttp3_qpack_lookup_result -nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, - nghttp3_qpack_indexing_mode indexing_mode); - -/* - * nghttp3_qpack_encoder_lookup_dtable searches |nv| in dynamic table. - * |token| is a token of nv->name and it is -1 if there is no - * corresponding token defined. |hash| is a hash of nv->name. - * |indexing_mode| provides indexing strategy. |krcnt| is Known - * Received Count. |allow_blocking| is nonzero if this stream can be - * blocked (or it has been blocked already). - */ -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_encoder_write_field_section_prefix writes Encoded - * Field Section Prefix into |pbuf|. |ricnt| is Required Insert - * Count. |base| is Base. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_field_section_prefix( - nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, - uint64_t base); - -/* - * nghttp3_qpack_encoder_write_static_indexed writes Indexed Header - * Field to |rbuf|. |absidx| is an absolute index into static table. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, - nghttp3_buf *rbuf, - uint64_t absidx); - -/* - * nghttp3_qpack_encoder_write_dynamic_indexed writes Indexed Header - * Field to |rbuf|. |absidx| is an absolute index into dynamic table. - * |base| is base. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, - nghttp3_buf *rbuf, - uint64_t absidx, uint64_t base); - -/* - * nghttp3_qpack_encoder_write_static_indexed writes Literal Header - * Field With Name Reference to |rbuf|. |absidx| is an absolute index - * into static table to reference a name. |nv| is a header field to - * encode. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_static_indexed_name( - nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, - const nghttp3_nv *nv); - -/* - * nghttp3_qpack_encoder_write_dynamic_indexed writes Literal Header - * Field With Name Reference to |rbuf|. |absidx| is an absolute index - * into dynamic table to reference a name. |base| is a base. |nv| is - * a header field to encode. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_dynamic_indexed_name( - nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, - uint64_t base, const nghttp3_nv *nv); - -/* - * nghttp3_qpack_encoder_write_literal writes Literal Header Field - * Without Name Reference to |rbuf|. |nv| is a header field to - * encode. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, - nghttp3_buf *rbuf, - const nghttp3_nv *nv); - -/* - * nghttp3_qpack_encoder_write_static_insert writes Insert With Name - * Reference to |ebuf|. |absidx| is an absolute index into static - * table to reference a name. |nv| is a header field to insert. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - uint64_t absidx, - const nghttp3_nv *nv); - -/* - * nghttp3_qpack_encoder_write_dynamic_insert writes Insert With Name - * Reference to |ebuf|. |absidx| is an absolute index into dynamic - * table to reference a name. |nv| is a header field to insert. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - uint64_t absidx, - const nghttp3_nv *nv); - -/* - * nghttp3_qpack_encoder_write_duplicate_insert writes Duplicate to - * |ebuf|. |absidx| is an absolute index into dynamic table to - * reference an entry. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - uint64_t absidx); - -/* - * nghttp3_qpack_encoder_write_literal_insert writes Insert Without - * Name Reference to |ebuf|. |nv| is a header field to insert. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, - const nghttp3_nv *nv); - -int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, - nghttp3_qpack_stream *stream); - -/* - * nghttp3_qpack_encoder_block_stream blocks |stream|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, - nghttp3_qpack_stream *stream); - -/* - * nghttp3_qpack_encoder_unblock_stream unblocks |stream|. - */ -void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, - nghttp3_qpack_stream *stream); - -/* - * nghttp3_qpack_encoder_unblock unblocks stream whose max_cnt is less - * than or equal to |max_cnt|. - */ -void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, - uint64_t max_cnt); - -/* - * nghttp3_qpack_encoder_find_stream returns stream whose stream ID is - * |stream_id|. This function returns NULL if there is no such - * stream. - */ -nghttp3_qpack_stream * -nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, - int64_t stream_id); - -uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder); - -/* - * nghttp3_qpack_encoder_shrink_dtable shrinks dynamic table so that - * the dynamic table size is less than or equal to maximum size. - */ -void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder); - -/* - * nghttp3_qpack_encoder_process_dtable_update processes pending - * dynamic table size update. It might write encoder stream into - * |ebuf|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf); - -/* - * nghttp3_qpack_encoder_write_set_dtable_cap writes Set Dynamic Table - * Capacity. to |ebuf|. |cap| is the capacity of dynamic table. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, - nghttp3_buf *ebuf, size_t cap); - -/* - * nghttp3_qpack_context_dtable_add adds |qnv| to dynamic table. If - * |ctx| is a part of encoder, |dtable_map| is not NULL. |hash| is a - * hash value of name. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, - nghttp3_qpack_nv *qnv, - nghttp3_qpack_map *dtable_map, - uint32_t hash); - -/* - * nghttp3_qpack_encoder_dtable_static_add adds |nv| to dynamic table - * by referencing static table entry at an absolute index |absidx|. - * The hash of name is given as |hash|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, - uint64_t absidx, - const nghttp3_nv *nv, - uint32_t hash); - -/* - * nghttp3_qpack_encoder_dtable_dynamic_add adds |nv| to dynamic table - * by referencing dynamic table entry at an absolute index |absidx|. - * The hash of name is given as |hash|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, - uint64_t absidx, - const nghttp3_nv *nv, - uint32_t hash); - -/* - * nghttp3_qpack_encoder_dtable_duplicate_add duplicates dynamic table - * entry at an absolute index |absidx|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, - uint64_t absidx); - -/* - * nghttp3_qpack_encoder_dtable_literal_add adds |nv| to dynamic - * table. |token| is a token of name and is -1 if it has no token - * value defined. |hash| is a hash of name. - * - * NGHTTP3_ERR_NOMEM Out of memory. - */ -int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, - const nghttp3_nv *nv, - int32_t token, uint32_t hash); - -/* - * nghttp3_qpack_context_dtable_get returns dynamic table entry whose - * absolute index is |absidx|. This function assumes that such entry - * exists. - */ -nghttp3_qpack_entry * -nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx); - -/* - * nghttp3_qpack_context_dtable_top returns latest dynamic table - * entry. This function assumes dynamic table is not empty. - */ -nghttp3_qpack_entry * -nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx); - -/* - * nghttp3_qpack_entry_init initializes |ent|. |qnv| is a header - * field. |sum| is the sum of table space occupied by all entries - * inserted so far. It does not include this entry. |absidx| is an - * absolute index of this entry. |hash| is a hash of header field - * name. This function increases reference count of qnv->nv.name and - * qnv->nv.value. - */ -void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, - size_t sum, uint64_t absidx, uint32_t hash); - -/* - * nghttp3_qpack_entry_free frees memory allocated for |ent|. - */ -void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent); - -/* - * nghttp3_qpack_put_varint_len returns the required number of bytes - * to encode |n| with |prefix| bits. - */ -size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix); - -/* - * nghttp3_qpack_put_varint encodes |n| using variable integer - * encoding with |prefix| bits into |buf|. This function assumes the - * buffer pointed by |buf| has enough space. This function returns - * the one byte beyond the last write (buf + - * nghttp3_qpack_put_varint_len(n, prefix)). - */ -uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix); - -/* nghttp3_qpack_encoder_stream_state is a set of states for encoder - stream decoding. */ -typedef enum { - NGHTTP3_QPACK_ES_STATE_OPCODE, - NGHTTP3_QPACK_ES_STATE_READ_INDEX, - NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN, - NGHTTP3_QPACK_ES_STATE_READ_NAMELEN, - NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN, - NGHTTP3_QPACK_ES_STATE_READ_NAME, - NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN, - NGHTTP3_QPACK_ES_STATE_READ_VALUELEN, - NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN, - NGHTTP3_QPACK_ES_STATE_READ_VALUE, -} nghttp3_qpack_encoder_stream_state; - -/* nghttp3_qpack_encoder_stream_opcode is a set of opcodes used in - encoder stream. */ -typedef enum { - NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED, - NGHTTP3_QPACK_ES_OPCODE_INSERT, - NGHTTP3_QPACK_ES_OPCODE_DUPLICATE, - NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP, -} nghttp3_qpack_encoder_stream_opcode; - -/* nghttp3_qpack_request_stream_state is a set of states for request - stream decoding. */ -typedef enum { - NGHTTP3_QPACK_RS_STATE_RICNT, - NGHTTP3_QPACK_RS_STATE_DBASE_SIGN, - NGHTTP3_QPACK_RS_STATE_DBASE, - NGHTTP3_QPACK_RS_STATE_OPCODE, - NGHTTP3_QPACK_RS_STATE_READ_INDEX, - NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN, - NGHTTP3_QPACK_RS_STATE_READ_NAMELEN, - NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN, - NGHTTP3_QPACK_RS_STATE_READ_NAME, - NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN, - NGHTTP3_QPACK_RS_STATE_READ_VALUELEN, - NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN, - NGHTTP3_QPACK_RS_STATE_READ_VALUE, - NGHTTP3_QPACK_RS_STATE_BLOCKED, -} nghttp3_qpack_request_stream_state; - -/* nghttp3_qpack_request_stream_opcode is a set of opcodes used in - request stream. */ -typedef enum { - NGHTTP3_QPACK_RS_OPCODE_INDEXED, - NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB, - NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME, - NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB, - NGHTTP3_QPACK_RS_OPCODE_LITERAL, -} nghttp3_qpack_request_stream_opcode; - -struct nghttp3_qpack_decoder { - nghttp3_qpack_context ctx; - /* state is a current state of reading encoder stream. */ - nghttp3_qpack_encoder_stream_state state; - /* opcode is an encoder stream opcode being processed. */ - nghttp3_qpack_encoder_stream_opcode opcode; - /* rstate is a set of intermediate state which are used to process - encoder stream. */ - nghttp3_qpack_read_state rstate; - /* dbuf is decoder stream. */ - nghttp3_buf dbuf; - /* written_icnt is Insert Count written to decoder stream so far. */ - uint64_t written_icnt; - /* max_concurrent_streams is the number of concurrent streams that a - remote endpoint can open, including both bidirectional and - unidirectional streams which potentially receives QPACK encoded - HEADER frame. */ - size_t max_concurrent_streams; -}; - -/* - * nghttp3_qpack_decoder_init initializes |decoder|. - * |max_dtable_size| is the maximum size of dynamic table. - * |max_blocked| 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 max_dtable_size, size_t max_blocked, - const nghttp3_mem *mem); - -/* - * nghttp3_qpack_decoder_free frees memory allocated for |decoder|. - * This function does not free memory pointed by |decoder|. - */ -void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder); - -/* - * nghttp3_qpack_decoder_dtable_indexed_add adds entry received in - * Insert With Name Reference to dynamic table. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - * NGHTTP3_ERR_QPACK_ENCODER_STREAM - * Space required for a decoded entry exceeds max dynamic table - * size. - */ -int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder); - -/* - * nghttp3_qpack_decoder_dtable_static_add adds entry received in - * Insert With Name Reference (static) to dynamic table. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - * NGHTTP3_ERR_QPACK_ENCODER_STREAM - * Space required for a decoded entry exceeds max dynamic table - * size. - */ -int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder); - -/* - * nghttp3_qpack_decoder_dtable_dynamic_add adds entry received in - * Insert With Name Reference (dynamic) to dynamic table. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - * NGHTTP3_ERR_QPACK_ENCODER_STREAM - * Space required for a decoded entry exceeds max dynamic table - * size. - */ -int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder); - -/* - * nghttp3_qpack_decoder_dtable_duplicate_add adds entry received in - * Duplicate to dynamic table. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - * NGHTTP3_ERR_QPACK_ENCODER_STREAM - * Space required for a decoded entry exceeds max dynamic table - * size. - */ -int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder); - -/* - * nghttp3_qpack_decoder_dtable_literal_add adds entry received in - * Insert Without Name Reference to dynamic table. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - * NGHTTP3_ERR_QPACK_ENCODER_STREAM - * Space required for a decoded entry exceeds max dynamic table - * size. - */ -int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder); - -struct nghttp3_qpack_stream_context { - /* state is a current state of reading request stream. */ - nghttp3_qpack_request_stream_state state; - /* rstate is a set of intermediate state which are used to process - request stream. */ - nghttp3_qpack_read_state rstate; - const nghttp3_mem *mem; - /* opcode is a request stream opcode being processed. */ - nghttp3_qpack_request_stream_opcode opcode; - int64_t stream_id; - /* ricnt is Required Insert Count to decode this header block. */ - uint64_t ricnt; - /* base is Base in Header Block Prefix. */ - uint64_t base; - /* dbase_sign is the delta base sign in Header Block Prefix. */ - int dbase_sign; -}; - -/* - * nghttp3_qpack_stream_context_init initializes |sctx|. - */ -void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx, - int64_t stream_id, - const nghttp3_mem *mem); - -/* - * nghttp3_qpack_stream_context_free frees memory allocated for - * |sctx|. This function does not free memory pointed by |sctx|. - */ -void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx); - -void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx); - -/* - * nghttp3_qpack_decoder_reconstruct_ricnt reconstructs Required - * Insert Count from the encoded form |encricnt| and stores Required - * Insert Count in |*dest|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED - * Unable to reconstruct Required Insert Count. - */ -int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, - uint64_t *dest, uint64_t encricnt); - -/* - * nghttp3_qpack_decoder_rel2abs converts relative index rstate->left - * received in encoder stream to absolute index and stores it in - * rstate->absidx. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_QPACK_ENCODER_STREAM - * Relative index is invalid. - */ -int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_read_state *rstate); - -/* - * nghttp3_qpack_decoder_brel2abs converts Base relative index - * rstate->left received in request stream to absolute index and - * stores it in rstate->absidx. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED - * Base relative index is invalid. - */ -int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx); - -/* - * nghttp3_qpack_decoder_pbrel2abs converts Post-Base relative index - * rstate->left received in request stream to absolute index and - * stores it in rstate->absidx. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED - * Post-Base relative index is invalid. - */ -int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx); - -void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv); - -void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv); - -void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv); - -/* - * nghttp3_qpack_decoder_write_section_ack writes Section - * Acknowledgement to decoder stream. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - * NGHTTP3_ERR_QPACK_FATAL - * Decoder stream overflow. - */ -int nghttp3_qpack_decoder_write_section_ack( - nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx); - -#endif /* NGHTTP3_QPACK_H */ diff --git a/deps/nghttp3/lib/nghttp3_qpack_huffman.c b/deps/nghttp3/lib/nghttp3_qpack_huffman.c deleted file mode 100644 index c36a68ededd..00000000000 --- a/deps/nghttp3/lib/nghttp3_qpack_huffman.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2013 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 "nghttp3_qpack_huffman.h" - -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "nghttp3_conv.h" - -size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len) { - size_t i; - size_t nbits = 0; - - for (i = 0; i < len; ++i) { - nbits += huffman_sym_table[src[i]].nbits; - } - /* pad the prefix of EOS (256) */ - return (nbits + 7) / 8; -} - -uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src, - size_t srclen) { - const nghttp3_qpack_huffman_sym *sym; - const uint8_t *end = src + srclen; - uint64_t code = 0; - size_t nbits = 0; - uint32_t x; - - for (; src != end;) { - sym = &huffman_sym_table[*src++]; - code |= (uint64_t)sym->code << (32 - nbits); - nbits += sym->nbits; - if (nbits < 32) { - continue; - } - x = htonl((uint32_t)(code >> 32)); - memcpy(dest, &x, 4); - dest += 4; - code <<= 32; - nbits -= 32; - } - - for (; nbits >= 8;) { - *dest++ = (uint8_t)(code >> 56); - code <<= 8; - nbits -= 8; - } - - if (nbits) { - *dest++ = (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1)); - } - - return dest; -} - -void nghttp3_qpack_huffman_decode_context_init( - nghttp3_qpack_huffman_decode_context *ctx) { - ctx->fstate = NGHTTP3_QPACK_HUFFMAN_ACCEPTED; -} - -nghttp3_ssize -nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, - uint8_t *dest, const uint8_t *src, size_t srclen, - int fin) { - uint8_t *p = dest; - const uint8_t *end = src + srclen; - nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0}; - const nghttp3_qpack_huffman_decode_node *t = &node; - uint8_t c; - - /* We use the decoding algorithm described in - http://graphics.ics.uci.edu/pub/Prefix.pdf */ - for (; src != end;) { - c = *src++; - t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c >> 4]; - if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) { - *p++ = t->sym; - } - - t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c & 0xf]; - if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) { - *p++ = t->sym; - } - } - - ctx->fstate = t->fstate; - - if (fin && !(ctx->fstate & NGHTTP3_QPACK_HUFFMAN_ACCEPTED)) { - return NGHTTP3_ERR_QPACK_FATAL; - } - - return p - dest; -} - -int nghttp3_qpack_huffman_decode_failure_state( - nghttp3_qpack_huffman_decode_context *ctx) { - return ctx->fstate == 0x100; -} diff --git a/deps/nghttp3/lib/nghttp3_qpack_huffman.h b/deps/nghttp3/lib/nghttp3_qpack_huffman.h deleted file mode 100644 index 0cab6ed93e6..00000000000 --- a/deps/nghttp3/lib/nghttp3_qpack_huffman.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2013 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. - */ -#ifndef NGHTTP3_QPACK_HUFFMAN_H -#define NGHTTP3_QPACK_HUFFMAN_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -typedef struct { - /* The number of bits in this code */ - uint32_t nbits; - /* Huffman code aligned to LSB */ - uint32_t code; -} nghttp3_qpack_huffman_sym; - -extern const nghttp3_qpack_huffman_sym huffman_sym_table[]; - -size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len); - -uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src, - size_t srclen); - -typedef enum { - /* FSA accepts this state as the end of huffman encoding - sequence. */ - NGHTTP3_QPACK_HUFFMAN_ACCEPTED = 1 << 14, - /* This state emits symbol */ - NGHTTP3_QPACK_HUFFMAN_SYM = 1 << 15, -} nghttp3_qpack_huffman_decode_flag; - -typedef struct { - /* fstate is the current huffman decoding state, which is actually - the node ID of internal huffman tree with - nghttp3_qpack_huffman_decode_flag OR-ed. We have 257 leaf nodes, - but they are identical to root node other than emitting a symbol, - so we have 256 internal nodes [1..256], inclusive. The node ID - 256 is a special node and it is a terminal state that means - decoding failed. */ - uint16_t fstate; - /* symbol if NGHTTP3_QPACK_HUFFMAN_SYM flag set */ - uint8_t sym; -} nghttp3_qpack_huffman_decode_node; - -typedef struct { - /* fstate is the current huffman decoding state. */ - uint16_t fstate; -} nghttp3_qpack_huffman_decode_context; - -extern const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16]; - -void nghttp3_qpack_huffman_decode_context_init( - nghttp3_qpack_huffman_decode_context *ctx); - -/* - * nghttp3_qpack_huffman_decode decodes huffman encoded byte string - * stored in |src| of length |srclen|. |ctx| is a decoding context. - * |ctx| remembers the decoding state, and caller can call this - * function multiple times to feed each chunk of huffman encoded - * substring. |fin| must be nonzero if |src| contains the last chunk - * of huffman string. The decoded string is written to the buffer - * pointed by |dest|. This function assumes that the buffer pointed - * by |dest| contains enough memory to store decoded byte string. - * - * This function returns the number of bytes written to |dest|, or one - * of the following negative error codes: - * - * NGHTTP3_ERR_QPACK_FATAL - * Could not decode huffman string. - */ -nghttp3_ssize -nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, - uint8_t *dest, const uint8_t *src, size_t srclen, - int fin); - -/* - * nghttp3_qpack_huffman_decode_failure_state returns nonzero if |ctx| - * indicates that huffman decoding context is in failure state. - */ -int nghttp3_qpack_huffman_decode_failure_state( - nghttp3_qpack_huffman_decode_context *ctx); - -#endif /* NGHTTP3_QPACK_HUFFMAN_H */ diff --git a/deps/nghttp3/lib/nghttp3_qpack_huffman_data.c b/deps/nghttp3/lib/nghttp3_qpack_huffman_data.c deleted file mode 100644 index 0c104dbc0a0..00000000000 --- a/deps/nghttp3/lib/nghttp3_qpack_huffman_data.c +++ /dev/null @@ -1,4981 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2013 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 "nghttp3_qpack_huffman.h" - -/* Generated by mkhufftbl.py */ - -const nghttp3_qpack_huffman_sym huffman_sym_table[] = { - {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, - {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, - {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, - {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, - {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, - {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, - {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, - {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, - {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, - {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, - {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, - {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, - {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, - {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, - {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, - {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, - {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, - {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, - {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, - {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, - {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, - {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, - {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, - {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, - {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, - {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, - {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, - {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, - {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, - {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, - {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, - {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, - {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, - {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, - {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, - {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, - {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, - {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, - {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, - {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, - {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, - {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, - {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, - {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, - {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, - {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, - {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, - {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, - {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, - {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, - {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, - {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, - {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, - {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, - {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, - {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, - {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, - {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, - {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, - {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, - {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, - {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, - {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, - {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, - {30, 0xfffffffcu}}; - -const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16] = { - /* 0 */ - { - {0x04, 0}, - {0x05, 0}, - {0x07, 0}, - {0x08, 0}, - {0x0b, 0}, - {0x0c, 0}, - {0x10, 0}, - {0x13, 0}, - {0x19, 0}, - {0x1c, 0}, - {0x20, 0}, - {0x23, 0}, - {0x2a, 0}, - {0x31, 0}, - {0x39, 0}, - {0x4040, 0}, - }, - /* 1 */ - { - {0xc000, 48}, - {0xc000, 49}, - {0xc000, 50}, - {0xc000, 97}, - {0xc000, 99}, - {0xc000, 101}, - {0xc000, 105}, - {0xc000, 111}, - {0xc000, 115}, - {0xc000, 116}, - {0x0d, 0}, - {0x0e, 0}, - {0x11, 0}, - {0x12, 0}, - {0x14, 0}, - {0x15, 0}, - }, - /* 2 */ - { - {0x8001, 48}, - {0xc016, 48}, - {0x8001, 49}, - {0xc016, 49}, - {0x8001, 50}, - {0xc016, 50}, - {0x8001, 97}, - {0xc016, 97}, - {0x8001, 99}, - {0xc016, 99}, - {0x8001, 101}, - {0xc016, 101}, - {0x8001, 105}, - {0xc016, 105}, - {0x8001, 111}, - {0xc016, 111}, - }, - /* 3 */ - { - {0x8002, 48}, - {0x8009, 48}, - {0x8017, 48}, - {0xc028, 48}, - {0x8002, 49}, - {0x8009, 49}, - {0x8017, 49}, - {0xc028, 49}, - {0x8002, 50}, - {0x8009, 50}, - {0x8017, 50}, - {0xc028, 50}, - {0x8002, 97}, - {0x8009, 97}, - {0x8017, 97}, - {0xc028, 97}, - }, - /* 4 */ - { - {0x8003, 48}, - {0x8006, 48}, - {0x800a, 48}, - {0x800f, 48}, - {0x8018, 48}, - {0x801f, 48}, - {0x8029, 48}, - {0xc038, 48}, - {0x8003, 49}, - {0x8006, 49}, - {0x800a, 49}, - {0x800f, 49}, - {0x8018, 49}, - {0x801f, 49}, - {0x8029, 49}, - {0xc038, 49}, - }, - /* 5 */ - { - {0x8003, 50}, - {0x8006, 50}, - {0x800a, 50}, - {0x800f, 50}, - {0x8018, 50}, - {0x801f, 50}, - {0x8029, 50}, - {0xc038, 50}, - {0x8003, 97}, - {0x8006, 97}, - {0x800a, 97}, - {0x800f, 97}, - {0x8018, 97}, - {0x801f, 97}, - {0x8029, 97}, - {0xc038, 97}, - }, - /* 6 */ - { - {0x8002, 99}, - {0x8009, 99}, - {0x8017, 99}, - {0xc028, 99}, - {0x8002, 101}, - {0x8009, 101}, - {0x8017, 101}, - {0xc028, 101}, - {0x8002, 105}, - {0x8009, 105}, - {0x8017, 105}, - {0xc028, 105}, - {0x8002, 111}, - {0x8009, 111}, - {0x8017, 111}, - {0xc028, 111}, - }, - /* 7 */ - { - {0x8003, 99}, - {0x8006, 99}, - {0x800a, 99}, - {0x800f, 99}, - {0x8018, 99}, - {0x801f, 99}, - {0x8029, 99}, - {0xc038, 99}, - {0x8003, 101}, - {0x8006, 101}, - {0x800a, 101}, - {0x800f, 101}, - {0x8018, 101}, - {0x801f, 101}, - {0x8029, 101}, - {0xc038, 101}, - }, - /* 8 */ - { - {0x8003, 105}, - {0x8006, 105}, - {0x800a, 105}, - {0x800f, 105}, - {0x8018, 105}, - {0x801f, 105}, - {0x8029, 105}, - {0xc038, 105}, - {0x8003, 111}, - {0x8006, 111}, - {0x800a, 111}, - {0x800f, 111}, - {0x8018, 111}, - {0x801f, 111}, - {0x8029, 111}, - {0xc038, 111}, - }, - /* 9 */ - { - {0x8001, 115}, - {0xc016, 115}, - {0x8001, 116}, - {0xc016, 116}, - {0xc000, 32}, - {0xc000, 37}, - {0xc000, 45}, - {0xc000, 46}, - {0xc000, 47}, - {0xc000, 51}, - {0xc000, 52}, - {0xc000, 53}, - {0xc000, 54}, - {0xc000, 55}, - {0xc000, 56}, - {0xc000, 57}, - }, - /* 10 */ - { - {0x8002, 115}, - {0x8009, 115}, - {0x8017, 115}, - {0xc028, 115}, - {0x8002, 116}, - {0x8009, 116}, - {0x8017, 116}, - {0xc028, 116}, - {0x8001, 32}, - {0xc016, 32}, - {0x8001, 37}, - {0xc016, 37}, - {0x8001, 45}, - {0xc016, 45}, - {0x8001, 46}, - {0xc016, 46}, - }, - /* 11 */ - { - {0x8003, 115}, - {0x8006, 115}, - {0x800a, 115}, - {0x800f, 115}, - {0x8018, 115}, - {0x801f, 115}, - {0x8029, 115}, - {0xc038, 115}, - {0x8003, 116}, - {0x8006, 116}, - {0x800a, 116}, - {0x800f, 116}, - {0x8018, 116}, - {0x801f, 116}, - {0x8029, 116}, - {0xc038, 116}, - }, - /* 12 */ - { - {0x8002, 32}, - {0x8009, 32}, - {0x8017, 32}, - {0xc028, 32}, - {0x8002, 37}, - {0x8009, 37}, - {0x8017, 37}, - {0xc028, 37}, - {0x8002, 45}, - {0x8009, 45}, - {0x8017, 45}, - {0xc028, 45}, - {0x8002, 46}, - {0x8009, 46}, - {0x8017, 46}, - {0xc028, 46}, - }, - /* 13 */ - { - {0x8003, 32}, - {0x8006, 32}, - {0x800a, 32}, - {0x800f, 32}, - {0x8018, 32}, - {0x801f, 32}, - {0x8029, 32}, - {0xc038, 32}, - {0x8003, 37}, - {0x8006, 37}, - {0x800a, 37}, - {0x800f, 37}, - {0x8018, 37}, - {0x801f, 37}, - {0x8029, 37}, - {0xc038, 37}, - }, - /* 14 */ - { - {0x8003, 45}, - {0x8006, 45}, - {0x800a, 45}, - {0x800f, 45}, - {0x8018, 45}, - {0x801f, 45}, - {0x8029, 45}, - {0xc038, 45}, - {0x8003, 46}, - {0x8006, 46}, - {0x800a, 46}, - {0x800f, 46}, - {0x8018, 46}, - {0x801f, 46}, - {0x8029, 46}, - {0xc038, 46}, - }, - /* 15 */ - { - {0x8001, 47}, - {0xc016, 47}, - {0x8001, 51}, - {0xc016, 51}, - {0x8001, 52}, - {0xc016, 52}, - {0x8001, 53}, - {0xc016, 53}, - {0x8001, 54}, - {0xc016, 54}, - {0x8001, 55}, - {0xc016, 55}, - {0x8001, 56}, - {0xc016, 56}, - {0x8001, 57}, - {0xc016, 57}, - }, - /* 16 */ - { - {0x8002, 47}, - {0x8009, 47}, - {0x8017, 47}, - {0xc028, 47}, - {0x8002, 51}, - {0x8009, 51}, - {0x8017, 51}, - {0xc028, 51}, - {0x8002, 52}, - {0x8009, 52}, - {0x8017, 52}, - {0xc028, 52}, - {0x8002, 53}, - {0x8009, 53}, - {0x8017, 53}, - {0xc028, 53}, - }, - /* 17 */ - { - {0x8003, 47}, - {0x8006, 47}, - {0x800a, 47}, - {0x800f, 47}, - {0x8018, 47}, - {0x801f, 47}, - {0x8029, 47}, - {0xc038, 47}, - {0x8003, 51}, - {0x8006, 51}, - {0x800a, 51}, - {0x800f, 51}, - {0x8018, 51}, - {0x801f, 51}, - {0x8029, 51}, - {0xc038, 51}, - }, - /* 18 */ - { - {0x8003, 52}, - {0x8006, 52}, - {0x800a, 52}, - {0x800f, 52}, - {0x8018, 52}, - {0x801f, 52}, - {0x8029, 52}, - {0xc038, 52}, - {0x8003, 53}, - {0x8006, 53}, - {0x800a, 53}, - {0x800f, 53}, - {0x8018, 53}, - {0x801f, 53}, - {0x8029, 53}, - {0xc038, 53}, - }, - /* 19 */ - { - {0x8002, 54}, - {0x8009, 54}, - {0x8017, 54}, - {0xc028, 54}, - {0x8002, 55}, - {0x8009, 55}, - {0x8017, 55}, - {0xc028, 55}, - {0x8002, 56}, - {0x8009, 56}, - {0x8017, 56}, - {0xc028, 56}, - {0x8002, 57}, - {0x8009, 57}, - {0x8017, 57}, - {0xc028, 57}, - }, - /* 20 */ - { - {0x8003, 54}, - {0x8006, 54}, - {0x800a, 54}, - {0x800f, 54}, - {0x8018, 54}, - {0x801f, 54}, - {0x8029, 54}, - {0xc038, 54}, - {0x8003, 55}, - {0x8006, 55}, - {0x800a, 55}, - {0x800f, 55}, - {0x8018, 55}, - {0x801f, 55}, - {0x8029, 55}, - {0xc038, 55}, - }, - /* 21 */ - { - {0x8003, 56}, - {0x8006, 56}, - {0x800a, 56}, - {0x800f, 56}, - {0x8018, 56}, - {0x801f, 56}, - {0x8029, 56}, - {0xc038, 56}, - {0x8003, 57}, - {0x8006, 57}, - {0x800a, 57}, - {0x800f, 57}, - {0x8018, 57}, - {0x801f, 57}, - {0x8029, 57}, - {0xc038, 57}, - }, - /* 22 */ - { - {0x1a, 0}, - {0x1b, 0}, - {0x1d, 0}, - {0x1e, 0}, - {0x21, 0}, - {0x22, 0}, - {0x24, 0}, - {0x25, 0}, - {0x2b, 0}, - {0x2e, 0}, - {0x32, 0}, - {0x35, 0}, - {0x3a, 0}, - {0x3d, 0}, - {0x41, 0}, - {0x4044, 0}, - }, - /* 23 */ - { - {0xc000, 61}, - {0xc000, 65}, - {0xc000, 95}, - {0xc000, 98}, - {0xc000, 100}, - {0xc000, 102}, - {0xc000, 103}, - {0xc000, 104}, - {0xc000, 108}, - {0xc000, 109}, - {0xc000, 110}, - {0xc000, 112}, - {0xc000, 114}, - {0xc000, 117}, - {0x26, 0}, - {0x27, 0}, - }, - /* 24 */ - { - {0x8001, 61}, - {0xc016, 61}, - {0x8001, 65}, - {0xc016, 65}, - {0x8001, 95}, - {0xc016, 95}, - {0x8001, 98}, - {0xc016, 98}, - {0x8001, 100}, - {0xc016, 100}, - {0x8001, 102}, - {0xc016, 102}, - {0x8001, 103}, - {0xc016, 103}, - {0x8001, 104}, - {0xc016, 104}, - }, - /* 25 */ - { - {0x8002, 61}, - {0x8009, 61}, - {0x8017, 61}, - {0xc028, 61}, - {0x8002, 65}, - {0x8009, 65}, - {0x8017, 65}, - {0xc028, 65}, - {0x8002, 95}, - {0x8009, 95}, - {0x8017, 95}, - {0xc028, 95}, - {0x8002, 98}, - {0x8009, 98}, - {0x8017, 98}, - {0xc028, 98}, - }, - /* 26 */ - { - {0x8003, 61}, - {0x8006, 61}, - {0x800a, 61}, - {0x800f, 61}, - {0x8018, 61}, - {0x801f, 61}, - {0x8029, 61}, - {0xc038, 61}, - {0x8003, 65}, - {0x8006, 65}, - {0x800a, 65}, - {0x800f, 65}, - {0x8018, 65}, - {0x801f, 65}, - {0x8029, 65}, - {0xc038, 65}, - }, - /* 27 */ - { - {0x8003, 95}, - {0x8006, 95}, - {0x800a, 95}, - {0x800f, 95}, - {0x8018, 95}, - {0x801f, 95}, - {0x8029, 95}, - {0xc038, 95}, - {0x8003, 98}, - {0x8006, 98}, - {0x800a, 98}, - {0x800f, 98}, - {0x8018, 98}, - {0x801f, 98}, - {0x8029, 98}, - {0xc038, 98}, - }, - /* 28 */ - { - {0x8002, 100}, - {0x8009, 100}, - {0x8017, 100}, - {0xc028, 100}, - {0x8002, 102}, - {0x8009, 102}, - {0x8017, 102}, - {0xc028, 102}, - {0x8002, 103}, - {0x8009, 103}, - {0x8017, 103}, - {0xc028, 103}, - {0x8002, 104}, - {0x8009, 104}, - {0x8017, 104}, - {0xc028, 104}, - }, - /* 29 */ - { - {0x8003, 100}, - {0x8006, 100}, - {0x800a, 100}, - {0x800f, 100}, - {0x8018, 100}, - {0x801f, 100}, - {0x8029, 100}, - {0xc038, 100}, - {0x8003, 102}, - {0x8006, 102}, - {0x800a, 102}, - {0x800f, 102}, - {0x8018, 102}, - {0x801f, 102}, - {0x8029, 102}, - {0xc038, 102}, - }, - /* 30 */ - { - {0x8003, 103}, - {0x8006, 103}, - {0x800a, 103}, - {0x800f, 103}, - {0x8018, 103}, - {0x801f, 103}, - {0x8029, 103}, - {0xc038, 103}, - {0x8003, 104}, - {0x8006, 104}, - {0x800a, 104}, - {0x800f, 104}, - {0x8018, 104}, - {0x801f, 104}, - {0x8029, 104}, - {0xc038, 104}, - }, - /* 31 */ - { - {0x8001, 108}, - {0xc016, 108}, - {0x8001, 109}, - {0xc016, 109}, - {0x8001, 110}, - {0xc016, 110}, - {0x8001, 112}, - {0xc016, 112}, - {0x8001, 114}, - {0xc016, 114}, - {0x8001, 117}, - {0xc016, 117}, - {0xc000, 58}, - {0xc000, 66}, - {0xc000, 67}, - {0xc000, 68}, - }, - /* 32 */ - { - {0x8002, 108}, - {0x8009, 108}, - {0x8017, 108}, - {0xc028, 108}, - {0x8002, 109}, - {0x8009, 109}, - {0x8017, 109}, - {0xc028, 109}, - {0x8002, 110}, - {0x8009, 110}, - {0x8017, 110}, - {0xc028, 110}, - {0x8002, 112}, - {0x8009, 112}, - {0x8017, 112}, - {0xc028, 112}, - }, - /* 33 */ - { - {0x8003, 108}, - {0x8006, 108}, - {0x800a, 108}, - {0x800f, 108}, - {0x8018, 108}, - {0x801f, 108}, - {0x8029, 108}, - {0xc038, 108}, - {0x8003, 109}, - {0x8006, 109}, - {0x800a, 109}, - {0x800f, 109}, - {0x8018, 109}, - {0x801f, 109}, - {0x8029, 109}, - {0xc038, 109}, - }, - /* 34 */ - { - {0x8003, 110}, - {0x8006, 110}, - {0x800a, 110}, - {0x800f, 110}, - {0x8018, 110}, - {0x801f, 110}, - {0x8029, 110}, - {0xc038, 110}, - {0x8003, 112}, - {0x8006, 112}, - {0x800a, 112}, - {0x800f, 112}, - {0x8018, 112}, - {0x801f, 112}, - {0x8029, 112}, - {0xc038, 112}, - }, - /* 35 */ - { - {0x8002, 114}, - {0x8009, 114}, - {0x8017, 114}, - {0xc028, 114}, - {0x8002, 117}, - {0x8009, 117}, - {0x8017, 117}, - {0xc028, 117}, - {0x8001, 58}, - {0xc016, 58}, - {0x8001, 66}, - {0xc016, 66}, - {0x8001, 67}, - {0xc016, 67}, - {0x8001, 68}, - {0xc016, 68}, - }, - /* 36 */ - { - {0x8003, 114}, - {0x8006, 114}, - {0x800a, 114}, - {0x800f, 114}, - {0x8018, 114}, - {0x801f, 114}, - {0x8029, 114}, - {0xc038, 114}, - {0x8003, 117}, - {0x8006, 117}, - {0x800a, 117}, - {0x800f, 117}, - {0x8018, 117}, - {0x801f, 117}, - {0x8029, 117}, - {0xc038, 117}, - }, - /* 37 */ - { - {0x8002, 58}, - {0x8009, 58}, - {0x8017, 58}, - {0xc028, 58}, - {0x8002, 66}, - {0x8009, 66}, - {0x8017, 66}, - {0xc028, 66}, - {0x8002, 67}, - {0x8009, 67}, - {0x8017, 67}, - {0xc028, 67}, - {0x8002, 68}, - {0x8009, 68}, - {0x8017, 68}, - {0xc028, 68}, - }, - /* 38 */ - { - {0x8003, 58}, - {0x8006, 58}, - {0x800a, 58}, - {0x800f, 58}, - {0x8018, 58}, - {0x801f, 58}, - {0x8029, 58}, - {0xc038, 58}, - {0x8003, 66}, - {0x8006, 66}, - {0x800a, 66}, - {0x800f, 66}, - {0x8018, 66}, - {0x801f, 66}, - {0x8029, 66}, - {0xc038, 66}, - }, - /* 39 */ - { - {0x8003, 67}, - {0x8006, 67}, - {0x800a, 67}, - {0x800f, 67}, - {0x8018, 67}, - {0x801f, 67}, - {0x8029, 67}, - {0xc038, 67}, - {0x8003, 68}, - {0x8006, 68}, - {0x800a, 68}, - {0x800f, 68}, - {0x8018, 68}, - {0x801f, 68}, - {0x8029, 68}, - {0xc038, 68}, - }, - /* 40 */ - { - {0x2c, 0}, - {0x2d, 0}, - {0x2f, 0}, - {0x30, 0}, - {0x33, 0}, - {0x34, 0}, - {0x36, 0}, - {0x37, 0}, - {0x3b, 0}, - {0x3c, 0}, - {0x3e, 0}, - {0x3f, 0}, - {0x42, 0}, - {0x43, 0}, - {0x45, 0}, - {0x4048, 0}, - }, - /* 41 */ - { - {0xc000, 69}, - {0xc000, 70}, - {0xc000, 71}, - {0xc000, 72}, - {0xc000, 73}, - {0xc000, 74}, - {0xc000, 75}, - {0xc000, 76}, - {0xc000, 77}, - {0xc000, 78}, - {0xc000, 79}, - {0xc000, 80}, - {0xc000, 81}, - {0xc000, 82}, - {0xc000, 83}, - {0xc000, 84}, - }, - /* 42 */ - { - {0x8001, 69}, - {0xc016, 69}, - {0x8001, 70}, - {0xc016, 70}, - {0x8001, 71}, - {0xc016, 71}, - {0x8001, 72}, - {0xc016, 72}, - {0x8001, 73}, - {0xc016, 73}, - {0x8001, 74}, - {0xc016, 74}, - {0x8001, 75}, - {0xc016, 75}, - {0x8001, 76}, - {0xc016, 76}, - }, - /* 43 */ - { - {0x8002, 69}, - {0x8009, 69}, - {0x8017, 69}, - {0xc028, 69}, - {0x8002, 70}, - {0x8009, 70}, - {0x8017, 70}, - {0xc028, 70}, - {0x8002, 71}, - {0x8009, 71}, - {0x8017, 71}, - {0xc028, 71}, - {0x8002, 72}, - {0x8009, 72}, - {0x8017, 72}, - {0xc028, 72}, - }, - /* 44 */ - { - {0x8003, 69}, - {0x8006, 69}, - {0x800a, 69}, - {0x800f, 69}, - {0x8018, 69}, - {0x801f, 69}, - {0x8029, 69}, - {0xc038, 69}, - {0x8003, 70}, - {0x8006, 70}, - {0x800a, 70}, - {0x800f, 70}, - {0x8018, 70}, - {0x801f, 70}, - {0x8029, 70}, - {0xc038, 70}, - }, - /* 45 */ - { - {0x8003, 71}, - {0x8006, 71}, - {0x800a, 71}, - {0x800f, 71}, - {0x8018, 71}, - {0x801f, 71}, - {0x8029, 71}, - {0xc038, 71}, - {0x8003, 72}, - {0x8006, 72}, - {0x800a, 72}, - {0x800f, 72}, - {0x8018, 72}, - {0x801f, 72}, - {0x8029, 72}, - {0xc038, 72}, - }, - /* 46 */ - { - {0x8002, 73}, - {0x8009, 73}, - {0x8017, 73}, - {0xc028, 73}, - {0x8002, 74}, - {0x8009, 74}, - {0x8017, 74}, - {0xc028, 74}, - {0x8002, 75}, - {0x8009, 75}, - {0x8017, 75}, - {0xc028, 75}, - {0x8002, 76}, - {0x8009, 76}, - {0x8017, 76}, - {0xc028, 76}, - }, - /* 47 */ - { - {0x8003, 73}, - {0x8006, 73}, - {0x800a, 73}, - {0x800f, 73}, - {0x8018, 73}, - {0x801f, 73}, - {0x8029, 73}, - {0xc038, 73}, - {0x8003, 74}, - {0x8006, 74}, - {0x800a, 74}, - {0x800f, 74}, - {0x8018, 74}, - {0x801f, 74}, - {0x8029, 74}, - {0xc038, 74}, - }, - /* 48 */ - { - {0x8003, 75}, - {0x8006, 75}, - {0x800a, 75}, - {0x800f, 75}, - {0x8018, 75}, - {0x801f, 75}, - {0x8029, 75}, - {0xc038, 75}, - {0x8003, 76}, - {0x8006, 76}, - {0x800a, 76}, - {0x800f, 76}, - {0x8018, 76}, - {0x801f, 76}, - {0x8029, 76}, - {0xc038, 76}, - }, - /* 49 */ - { - {0x8001, 77}, - {0xc016, 77}, - {0x8001, 78}, - {0xc016, 78}, - {0x8001, 79}, - {0xc016, 79}, - {0x8001, 80}, - {0xc016, 80}, - {0x8001, 81}, - {0xc016, 81}, - {0x8001, 82}, - {0xc016, 82}, - {0x8001, 83}, - {0xc016, 83}, - {0x8001, 84}, - {0xc016, 84}, - }, - /* 50 */ - { - {0x8002, 77}, - {0x8009, 77}, - {0x8017, 77}, - {0xc028, 77}, - {0x8002, 78}, - {0x8009, 78}, - {0x8017, 78}, - {0xc028, 78}, - {0x8002, 79}, - {0x8009, 79}, - {0x8017, 79}, - {0xc028, 79}, - {0x8002, 80}, - {0x8009, 80}, - {0x8017, 80}, - {0xc028, 80}, - }, - /* 51 */ - { - {0x8003, 77}, - {0x8006, 77}, - {0x800a, 77}, - {0x800f, 77}, - {0x8018, 77}, - {0x801f, 77}, - {0x8029, 77}, - {0xc038, 77}, - {0x8003, 78}, - {0x8006, 78}, - {0x800a, 78}, - {0x800f, 78}, - {0x8018, 78}, - {0x801f, 78}, - {0x8029, 78}, - {0xc038, 78}, - }, - /* 52 */ - { - {0x8003, 79}, - {0x8006, 79}, - {0x800a, 79}, - {0x800f, 79}, - {0x8018, 79}, - {0x801f, 79}, - {0x8029, 79}, - {0xc038, 79}, - {0x8003, 80}, - {0x8006, 80}, - {0x800a, 80}, - {0x800f, 80}, - {0x8018, 80}, - {0x801f, 80}, - {0x8029, 80}, - {0xc038, 80}, - }, - /* 53 */ - { - {0x8002, 81}, - {0x8009, 81}, - {0x8017, 81}, - {0xc028, 81}, - {0x8002, 82}, - {0x8009, 82}, - {0x8017, 82}, - {0xc028, 82}, - {0x8002, 83}, - {0x8009, 83}, - {0x8017, 83}, - {0xc028, 83}, - {0x8002, 84}, - {0x8009, 84}, - {0x8017, 84}, - {0xc028, 84}, - }, - /* 54 */ - { - {0x8003, 81}, - {0x8006, 81}, - {0x800a, 81}, - {0x800f, 81}, - {0x8018, 81}, - {0x801f, 81}, - {0x8029, 81}, - {0xc038, 81}, - {0x8003, 82}, - {0x8006, 82}, - {0x800a, 82}, - {0x800f, 82}, - {0x8018, 82}, - {0x801f, 82}, - {0x8029, 82}, - {0xc038, 82}, - }, - /* 55 */ - { - {0x8003, 83}, - {0x8006, 83}, - {0x800a, 83}, - {0x800f, 83}, - {0x8018, 83}, - {0x801f, 83}, - {0x8029, 83}, - {0xc038, 83}, - {0x8003, 84}, - {0x8006, 84}, - {0x800a, 84}, - {0x800f, 84}, - {0x8018, 84}, - {0x801f, 84}, - {0x8029, 84}, - {0xc038, 84}, - }, - /* 56 */ - { - {0xc000, 85}, - {0xc000, 86}, - {0xc000, 87}, - {0xc000, 89}, - {0xc000, 106}, - {0xc000, 107}, - {0xc000, 113}, - {0xc000, 118}, - {0xc000, 119}, - {0xc000, 120}, - {0xc000, 121}, - {0xc000, 122}, - {0x46, 0}, - {0x47, 0}, - {0x49, 0}, - {0x404a, 0}, - }, - /* 57 */ - { - {0x8001, 85}, - {0xc016, 85}, - {0x8001, 86}, - {0xc016, 86}, - {0x8001, 87}, - {0xc016, 87}, - {0x8001, 89}, - {0xc016, 89}, - {0x8001, 106}, - {0xc016, 106}, - {0x8001, 107}, - {0xc016, 107}, - {0x8001, 113}, - {0xc016, 113}, - {0x8001, 118}, - {0xc016, 118}, - }, - /* 58 */ - { - {0x8002, 85}, - {0x8009, 85}, - {0x8017, 85}, - {0xc028, 85}, - {0x8002, 86}, - {0x8009, 86}, - {0x8017, 86}, - {0xc028, 86}, - {0x8002, 87}, - {0x8009, 87}, - {0x8017, 87}, - {0xc028, 87}, - {0x8002, 89}, - {0x8009, 89}, - {0x8017, 89}, - {0xc028, 89}, - }, - /* 59 */ - { - {0x8003, 85}, - {0x8006, 85}, - {0x800a, 85}, - {0x800f, 85}, - {0x8018, 85}, - {0x801f, 85}, - {0x8029, 85}, - {0xc038, 85}, - {0x8003, 86}, - {0x8006, 86}, - {0x800a, 86}, - {0x800f, 86}, - {0x8018, 86}, - {0x801f, 86}, - {0x8029, 86}, - {0xc038, 86}, - }, - /* 60 */ - { - {0x8003, 87}, - {0x8006, 87}, - {0x800a, 87}, - {0x800f, 87}, - {0x8018, 87}, - {0x801f, 87}, - {0x8029, 87}, - {0xc038, 87}, - {0x8003, 89}, - {0x8006, 89}, - {0x800a, 89}, - {0x800f, 89}, - {0x8018, 89}, - {0x801f, 89}, - {0x8029, 89}, - {0xc038, 89}, - }, - /* 61 */ - { - {0x8002, 106}, - {0x8009, 106}, - {0x8017, 106}, - {0xc028, 106}, - {0x8002, 107}, - {0x8009, 107}, - {0x8017, 107}, - {0xc028, 107}, - {0x8002, 113}, - {0x8009, 113}, - {0x8017, 113}, - {0xc028, 113}, - {0x8002, 118}, - {0x8009, 118}, - {0x8017, 118}, - {0xc028, 118}, - }, - /* 62 */ - { - {0x8003, 106}, - {0x8006, 106}, - {0x800a, 106}, - {0x800f, 106}, - {0x8018, 106}, - {0x801f, 106}, - {0x8029, 106}, - {0xc038, 106}, - {0x8003, 107}, - {0x8006, 107}, - {0x800a, 107}, - {0x800f, 107}, - {0x8018, 107}, - {0x801f, 107}, - {0x8029, 107}, - {0xc038, 107}, - }, - /* 63 */ - { - {0x8003, 113}, - {0x8006, 113}, - {0x800a, 113}, - {0x800f, 113}, - {0x8018, 113}, - {0x801f, 113}, - {0x8029, 113}, - {0xc038, 113}, - {0x8003, 118}, - {0x8006, 118}, - {0x800a, 118}, - {0x800f, 118}, - {0x8018, 118}, - {0x801f, 118}, - {0x8029, 118}, - {0xc038, 118}, - }, - /* 64 */ - { - {0x8001, 119}, - {0xc016, 119}, - {0x8001, 120}, - {0xc016, 120}, - {0x8001, 121}, - {0xc016, 121}, - {0x8001, 122}, - {0xc016, 122}, - {0xc000, 38}, - {0xc000, 42}, - {0xc000, 44}, - {0xc000, 59}, - {0xc000, 88}, - {0xc000, 90}, - {0x4b, 0}, - {0x4e, 0}, - }, - /* 65 */ - { - {0x8002, 119}, - {0x8009, 119}, - {0x8017, 119}, - {0xc028, 119}, - {0x8002, 120}, - {0x8009, 120}, - {0x8017, 120}, - {0xc028, 120}, - {0x8002, 121}, - {0x8009, 121}, - {0x8017, 121}, - {0xc028, 121}, - {0x8002, 122}, - {0x8009, 122}, - {0x8017, 122}, - {0xc028, 122}, - }, - /* 66 */ - { - {0x8003, 119}, - {0x8006, 119}, - {0x800a, 119}, - {0x800f, 119}, - {0x8018, 119}, - {0x801f, 119}, - {0x8029, 119}, - {0xc038, 119}, - {0x8003, 120}, - {0x8006, 120}, - {0x800a, 120}, - {0x800f, 120}, - {0x8018, 120}, - {0x801f, 120}, - {0x8029, 120}, - {0xc038, 120}, - }, - /* 67 */ - { - {0x8003, 121}, - {0x8006, 121}, - {0x800a, 121}, - {0x800f, 121}, - {0x8018, 121}, - {0x801f, 121}, - {0x8029, 121}, - {0xc038, 121}, - {0x8003, 122}, - {0x8006, 122}, - {0x800a, 122}, - {0x800f, 122}, - {0x8018, 122}, - {0x801f, 122}, - {0x8029, 122}, - {0xc038, 122}, - }, - /* 68 */ - { - {0x8001, 38}, - {0xc016, 38}, - {0x8001, 42}, - {0xc016, 42}, - {0x8001, 44}, - {0xc016, 44}, - {0x8001, 59}, - {0xc016, 59}, - {0x8001, 88}, - {0xc016, 88}, - {0x8001, 90}, - {0xc016, 90}, - {0x4c, 0}, - {0x4d, 0}, - {0x4f, 0}, - {0x51, 0}, - }, - /* 69 */ - { - {0x8002, 38}, - {0x8009, 38}, - {0x8017, 38}, - {0xc028, 38}, - {0x8002, 42}, - {0x8009, 42}, - {0x8017, 42}, - {0xc028, 42}, - {0x8002, 44}, - {0x8009, 44}, - {0x8017, 44}, - {0xc028, 44}, - {0x8002, 59}, - {0x8009, 59}, - {0x8017, 59}, - {0xc028, 59}, - }, - /* 70 */ - { - {0x8003, 38}, - {0x8006, 38}, - {0x800a, 38}, - {0x800f, 38}, - {0x8018, 38}, - {0x801f, 38}, - {0x8029, 38}, - {0xc038, 38}, - {0x8003, 42}, - {0x8006, 42}, - {0x800a, 42}, - {0x800f, 42}, - {0x8018, 42}, - {0x801f, 42}, - {0x8029, 42}, - {0xc038, 42}, - }, - /* 71 */ - { - {0x8003, 44}, - {0x8006, 44}, - {0x800a, 44}, - {0x800f, 44}, - {0x8018, 44}, - {0x801f, 44}, - {0x8029, 44}, - {0xc038, 44}, - {0x8003, 59}, - {0x8006, 59}, - {0x800a, 59}, - {0x800f, 59}, - {0x8018, 59}, - {0x801f, 59}, - {0x8029, 59}, - {0xc038, 59}, - }, - /* 72 */ - { - {0x8002, 88}, - {0x8009, 88}, - {0x8017, 88}, - {0xc028, 88}, - {0x8002, 90}, - {0x8009, 90}, - {0x8017, 90}, - {0xc028, 90}, - {0xc000, 33}, - {0xc000, 34}, - {0xc000, 40}, - {0xc000, 41}, - {0xc000, 63}, - {0x50, 0}, - {0x52, 0}, - {0x54, 0}, - }, - /* 73 */ - { - {0x8003, 88}, - {0x8006, 88}, - {0x800a, 88}, - {0x800f, 88}, - {0x8018, 88}, - {0x801f, 88}, - {0x8029, 88}, - {0xc038, 88}, - {0x8003, 90}, - {0x8006, 90}, - {0x800a, 90}, - {0x800f, 90}, - {0x8018, 90}, - {0x801f, 90}, - {0x8029, 90}, - {0xc038, 90}, - }, - /* 74 */ - { - {0x8001, 33}, - {0xc016, 33}, - {0x8001, 34}, - {0xc016, 34}, - {0x8001, 40}, - {0xc016, 40}, - {0x8001, 41}, - {0xc016, 41}, - {0x8001, 63}, - {0xc016, 63}, - {0xc000, 39}, - {0xc000, 43}, - {0xc000, 124}, - {0x53, 0}, - {0x55, 0}, - {0x58, 0}, - }, - /* 75 */ - { - {0x8002, 33}, - {0x8009, 33}, - {0x8017, 33}, - {0xc028, 33}, - {0x8002, 34}, - {0x8009, 34}, - {0x8017, 34}, - {0xc028, 34}, - {0x8002, 40}, - {0x8009, 40}, - {0x8017, 40}, - {0xc028, 40}, - {0x8002, 41}, - {0x8009, 41}, - {0x8017, 41}, - {0xc028, 41}, - }, - /* 76 */ - { - {0x8003, 33}, - {0x8006, 33}, - {0x800a, 33}, - {0x800f, 33}, - {0x8018, 33}, - {0x801f, 33}, - {0x8029, 33}, - {0xc038, 33}, - {0x8003, 34}, - {0x8006, 34}, - {0x800a, 34}, - {0x800f, 34}, - {0x8018, 34}, - {0x801f, 34}, - {0x8029, 34}, - {0xc038, 34}, - }, - /* 77 */ - { - {0x8003, 40}, - {0x8006, 40}, - {0x800a, 40}, - {0x800f, 40}, - {0x8018, 40}, - {0x801f, 40}, - {0x8029, 40}, - {0xc038, 40}, - {0x8003, 41}, - {0x8006, 41}, - {0x800a, 41}, - {0x800f, 41}, - {0x8018, 41}, - {0x801f, 41}, - {0x8029, 41}, - {0xc038, 41}, - }, - /* 78 */ - { - {0x8002, 63}, - {0x8009, 63}, - {0x8017, 63}, - {0xc028, 63}, - {0x8001, 39}, - {0xc016, 39}, - {0x8001, 43}, - {0xc016, 43}, - {0x8001, 124}, - {0xc016, 124}, - {0xc000, 35}, - {0xc000, 62}, - {0x56, 0}, - {0x57, 0}, - {0x59, 0}, - {0x5a, 0}, - }, - /* 79 */ - { - {0x8003, 63}, - {0x8006, 63}, - {0x800a, 63}, - {0x800f, 63}, - {0x8018, 63}, - {0x801f, 63}, - {0x8029, 63}, - {0xc038, 63}, - {0x8002, 39}, - {0x8009, 39}, - {0x8017, 39}, - {0xc028, 39}, - {0x8002, 43}, - {0x8009, 43}, - {0x8017, 43}, - {0xc028, 43}, - }, - /* 80 */ - { - {0x8003, 39}, - {0x8006, 39}, - {0x800a, 39}, - {0x800f, 39}, - {0x8018, 39}, - {0x801f, 39}, - {0x8029, 39}, - {0xc038, 39}, - {0x8003, 43}, - {0x8006, 43}, - {0x800a, 43}, - {0x800f, 43}, - {0x8018, 43}, - {0x801f, 43}, - {0x8029, 43}, - {0xc038, 43}, - }, - /* 81 */ - { - {0x8002, 124}, - {0x8009, 124}, - {0x8017, 124}, - {0xc028, 124}, - {0x8001, 35}, - {0xc016, 35}, - {0x8001, 62}, - {0xc016, 62}, - {0xc000, 0}, - {0xc000, 36}, - {0xc000, 64}, - {0xc000, 91}, - {0xc000, 93}, - {0xc000, 126}, - {0x5b, 0}, - {0x5c, 0}, - }, - /* 82 */ - { - {0x8003, 124}, - {0x8006, 124}, - {0x800a, 124}, - {0x800f, 124}, - {0x8018, 124}, - {0x801f, 124}, - {0x8029, 124}, - {0xc038, 124}, - {0x8002, 35}, - {0x8009, 35}, - {0x8017, 35}, - {0xc028, 35}, - {0x8002, 62}, - {0x8009, 62}, - {0x8017, 62}, - {0xc028, 62}, - }, - /* 83 */ - { - {0x8003, 35}, - {0x8006, 35}, - {0x800a, 35}, - {0x800f, 35}, - {0x8018, 35}, - {0x801f, 35}, - {0x8029, 35}, - {0xc038, 35}, - {0x8003, 62}, - {0x8006, 62}, - {0x800a, 62}, - {0x800f, 62}, - {0x8018, 62}, - {0x801f, 62}, - {0x8029, 62}, - {0xc038, 62}, - }, - /* 84 */ - { - {0x8001, 0}, - {0xc016, 0}, - {0x8001, 36}, - {0xc016, 36}, - {0x8001, 64}, - {0xc016, 64}, - {0x8001, 91}, - {0xc016, 91}, - {0x8001, 93}, - {0xc016, 93}, - {0x8001, 126}, - {0xc016, 126}, - {0xc000, 94}, - {0xc000, 125}, - {0x5d, 0}, - {0x5e, 0}, - }, - /* 85 */ - { - {0x8002, 0}, - {0x8009, 0}, - {0x8017, 0}, - {0xc028, 0}, - {0x8002, 36}, - {0x8009, 36}, - {0x8017, 36}, - {0xc028, 36}, - {0x8002, 64}, - {0x8009, 64}, - {0x8017, 64}, - {0xc028, 64}, - {0x8002, 91}, - {0x8009, 91}, - {0x8017, 91}, - {0xc028, 91}, - }, - /* 86 */ - { - {0x8003, 0}, - {0x8006, 0}, - {0x800a, 0}, - {0x800f, 0}, - {0x8018, 0}, - {0x801f, 0}, - {0x8029, 0}, - {0xc038, 0}, - {0x8003, 36}, - {0x8006, 36}, - {0x800a, 36}, - {0x800f, 36}, - {0x8018, 36}, - {0x801f, 36}, - {0x8029, 36}, - {0xc038, 36}, - }, - /* 87 */ - { - {0x8003, 64}, - {0x8006, 64}, - {0x800a, 64}, - {0x800f, 64}, - {0x8018, 64}, - {0x801f, 64}, - {0x8029, 64}, - {0xc038, 64}, - {0x8003, 91}, - {0x8006, 91}, - {0x800a, 91}, - {0x800f, 91}, - {0x8018, 91}, - {0x801f, 91}, - {0x8029, 91}, - {0xc038, 91}, - }, - /* 88 */ - { - {0x8002, 93}, - {0x8009, 93}, - {0x8017, 93}, - {0xc028, 93}, - {0x8002, 126}, - {0x8009, 126}, - {0x8017, 126}, - {0xc028, 126}, - {0x8001, 94}, - {0xc016, 94}, - {0x8001, 125}, - {0xc016, 125}, - {0xc000, 60}, - {0xc000, 96}, - {0xc000, 123}, - {0x5f, 0}, - }, - /* 89 */ - { - {0x8003, 93}, - {0x8006, 93}, - {0x800a, 93}, - {0x800f, 93}, - {0x8018, 93}, - {0x801f, 93}, - {0x8029, 93}, - {0xc038, 93}, - {0x8003, 126}, - {0x8006, 126}, - {0x800a, 126}, - {0x800f, 126}, - {0x8018, 126}, - {0x801f, 126}, - {0x8029, 126}, - {0xc038, 126}, - }, - /* 90 */ - { - {0x8002, 94}, - {0x8009, 94}, - {0x8017, 94}, - {0xc028, 94}, - {0x8002, 125}, - {0x8009, 125}, - {0x8017, 125}, - {0xc028, 125}, - {0x8001, 60}, - {0xc016, 60}, - {0x8001, 96}, - {0xc016, 96}, - {0x8001, 123}, - {0xc016, 123}, - {0x60, 0}, - {0x6e, 0}, - }, - /* 91 */ - { - {0x8003, 94}, - {0x8006, 94}, - {0x800a, 94}, - {0x800f, 94}, - {0x8018, 94}, - {0x801f, 94}, - {0x8029, 94}, - {0xc038, 94}, - {0x8003, 125}, - {0x8006, 125}, - {0x800a, 125}, - {0x800f, 125}, - {0x8018, 125}, - {0x801f, 125}, - {0x8029, 125}, - {0xc038, 125}, - }, - /* 92 */ - { - {0x8002, 60}, - {0x8009, 60}, - {0x8017, 60}, - {0xc028, 60}, - {0x8002, 96}, - {0x8009, 96}, - {0x8017, 96}, - {0xc028, 96}, - {0x8002, 123}, - {0x8009, 123}, - {0x8017, 123}, - {0xc028, 123}, - {0x61, 0}, - {0x65, 0}, - {0x6f, 0}, - {0x85, 0}, - }, - /* 93 */ - { - {0x8003, 60}, - {0x8006, 60}, - {0x800a, 60}, - {0x800f, 60}, - {0x8018, 60}, - {0x801f, 60}, - {0x8029, 60}, - {0xc038, 60}, - {0x8003, 96}, - {0x8006, 96}, - {0x800a, 96}, - {0x800f, 96}, - {0x8018, 96}, - {0x801f, 96}, - {0x8029, 96}, - {0xc038, 96}, - }, - /* 94 */ - { - {0x8003, 123}, - {0x8006, 123}, - {0x800a, 123}, - {0x800f, 123}, - {0x8018, 123}, - {0x801f, 123}, - {0x8029, 123}, - {0xc038, 123}, - {0x62, 0}, - {0x63, 0}, - {0x66, 0}, - {0x69, 0}, - {0x70, 0}, - {0x77, 0}, - {0x86, 0}, - {0x99, 0}, - }, - /* 95 */ - { - {0xc000, 92}, - {0xc000, 195}, - {0xc000, 208}, - {0x64, 0}, - {0x67, 0}, - {0x68, 0}, - {0x6a, 0}, - {0x6b, 0}, - {0x71, 0}, - {0x74, 0}, - {0x78, 0}, - {0x7e, 0}, - {0x87, 0}, - {0x8e, 0}, - {0x9a, 0}, - {0xa9, 0}, - }, - /* 96 */ - { - {0x8001, 92}, - {0xc016, 92}, - {0x8001, 195}, - {0xc016, 195}, - {0x8001, 208}, - {0xc016, 208}, - {0xc000, 128}, - {0xc000, 130}, - {0xc000, 131}, - {0xc000, 162}, - {0xc000, 184}, - {0xc000, 194}, - {0xc000, 224}, - {0xc000, 226}, - {0x6c, 0}, - {0x6d, 0}, - }, - /* 97 */ - { - {0x8002, 92}, - {0x8009, 92}, - {0x8017, 92}, - {0xc028, 92}, - {0x8002, 195}, - {0x8009, 195}, - {0x8017, 195}, - {0xc028, 195}, - {0x8002, 208}, - {0x8009, 208}, - {0x8017, 208}, - {0xc028, 208}, - {0x8001, 128}, - {0xc016, 128}, - {0x8001, 130}, - {0xc016, 130}, - }, - /* 98 */ - { - {0x8003, 92}, - {0x8006, 92}, - {0x800a, 92}, - {0x800f, 92}, - {0x8018, 92}, - {0x801f, 92}, - {0x8029, 92}, - {0xc038, 92}, - {0x8003, 195}, - {0x8006, 195}, - {0x800a, 195}, - {0x800f, 195}, - {0x8018, 195}, - {0x801f, 195}, - {0x8029, 195}, - {0xc038, 195}, - }, - /* 99 */ - { - {0x8003, 208}, - {0x8006, 208}, - {0x800a, 208}, - {0x800f, 208}, - {0x8018, 208}, - {0x801f, 208}, - {0x8029, 208}, - {0xc038, 208}, - {0x8002, 128}, - {0x8009, 128}, - {0x8017, 128}, - {0xc028, 128}, - {0x8002, 130}, - {0x8009, 130}, - {0x8017, 130}, - {0xc028, 130}, - }, - /* 100 */ - { - {0x8003, 128}, - {0x8006, 128}, - {0x800a, 128}, - {0x800f, 128}, - {0x8018, 128}, - {0x801f, 128}, - {0x8029, 128}, - {0xc038, 128}, - {0x8003, 130}, - {0x8006, 130}, - {0x800a, 130}, - {0x800f, 130}, - {0x8018, 130}, - {0x801f, 130}, - {0x8029, 130}, - {0xc038, 130}, - }, - /* 101 */ - { - {0x8001, 131}, - {0xc016, 131}, - {0x8001, 162}, - {0xc016, 162}, - {0x8001, 184}, - {0xc016, 184}, - {0x8001, 194}, - {0xc016, 194}, - {0x8001, 224}, - {0xc016, 224}, - {0x8001, 226}, - {0xc016, 226}, - {0xc000, 153}, - {0xc000, 161}, - {0xc000, 167}, - {0xc000, 172}, - }, - /* 102 */ - { - {0x8002, 131}, - {0x8009, 131}, - {0x8017, 131}, - {0xc028, 131}, - {0x8002, 162}, - {0x8009, 162}, - {0x8017, 162}, - {0xc028, 162}, - {0x8002, 184}, - {0x8009, 184}, - {0x8017, 184}, - {0xc028, 184}, - {0x8002, 194}, - {0x8009, 194}, - {0x8017, 194}, - {0xc028, 194}, - }, - /* 103 */ - { - {0x8003, 131}, - {0x8006, 131}, - {0x800a, 131}, - {0x800f, 131}, - {0x8018, 131}, - {0x801f, 131}, - {0x8029, 131}, - {0xc038, 131}, - {0x8003, 162}, - {0x8006, 162}, - {0x800a, 162}, - {0x800f, 162}, - {0x8018, 162}, - {0x801f, 162}, - {0x8029, 162}, - {0xc038, 162}, - }, - /* 104 */ - { - {0x8003, 184}, - {0x8006, 184}, - {0x800a, 184}, - {0x800f, 184}, - {0x8018, 184}, - {0x801f, 184}, - {0x8029, 184}, - {0xc038, 184}, - {0x8003, 194}, - {0x8006, 194}, - {0x800a, 194}, - {0x800f, 194}, - {0x8018, 194}, - {0x801f, 194}, - {0x8029, 194}, - {0xc038, 194}, - }, - /* 105 */ - { - {0x8002, 224}, - {0x8009, 224}, - {0x8017, 224}, - {0xc028, 224}, - {0x8002, 226}, - {0x8009, 226}, - {0x8017, 226}, - {0xc028, 226}, - {0x8001, 153}, - {0xc016, 153}, - {0x8001, 161}, - {0xc016, 161}, - {0x8001, 167}, - {0xc016, 167}, - {0x8001, 172}, - {0xc016, 172}, - }, - /* 106 */ - { - {0x8003, 224}, - {0x8006, 224}, - {0x800a, 224}, - {0x800f, 224}, - {0x8018, 224}, - {0x801f, 224}, - {0x8029, 224}, - {0xc038, 224}, - {0x8003, 226}, - {0x8006, 226}, - {0x800a, 226}, - {0x800f, 226}, - {0x8018, 226}, - {0x801f, 226}, - {0x8029, 226}, - {0xc038, 226}, - }, - /* 107 */ - { - {0x8002, 153}, - {0x8009, 153}, - {0x8017, 153}, - {0xc028, 153}, - {0x8002, 161}, - {0x8009, 161}, - {0x8017, 161}, - {0xc028, 161}, - {0x8002, 167}, - {0x8009, 167}, - {0x8017, 167}, - {0xc028, 167}, - {0x8002, 172}, - {0x8009, 172}, - {0x8017, 172}, - {0xc028, 172}, - }, - /* 108 */ - { - {0x8003, 153}, - {0x8006, 153}, - {0x800a, 153}, - {0x800f, 153}, - {0x8018, 153}, - {0x801f, 153}, - {0x8029, 153}, - {0xc038, 153}, - {0x8003, 161}, - {0x8006, 161}, - {0x800a, 161}, - {0x800f, 161}, - {0x8018, 161}, - {0x801f, 161}, - {0x8029, 161}, - {0xc038, 161}, - }, - /* 109 */ - { - {0x8003, 167}, - {0x8006, 167}, - {0x800a, 167}, - {0x800f, 167}, - {0x8018, 167}, - {0x801f, 167}, - {0x8029, 167}, - {0xc038, 167}, - {0x8003, 172}, - {0x8006, 172}, - {0x800a, 172}, - {0x800f, 172}, - {0x8018, 172}, - {0x801f, 172}, - {0x8029, 172}, - {0xc038, 172}, - }, - /* 110 */ - { - {0x72, 0}, - {0x73, 0}, - {0x75, 0}, - {0x76, 0}, - {0x79, 0}, - {0x7b, 0}, - {0x7f, 0}, - {0x82, 0}, - {0x88, 0}, - {0x8b, 0}, - {0x8f, 0}, - {0x92, 0}, - {0x9b, 0}, - {0xa2, 0}, - {0xaa, 0}, - {0xb4, 0}, - }, - /* 111 */ - { - {0xc000, 176}, - {0xc000, 177}, - {0xc000, 179}, - {0xc000, 209}, - {0xc000, 216}, - {0xc000, 217}, - {0xc000, 227}, - {0xc000, 229}, - {0xc000, 230}, - {0x7a, 0}, - {0x7c, 0}, - {0x7d, 0}, - {0x80, 0}, - {0x81, 0}, - {0x83, 0}, - {0x84, 0}, - }, - /* 112 */ - { - {0x8001, 176}, - {0xc016, 176}, - {0x8001, 177}, - {0xc016, 177}, - {0x8001, 179}, - {0xc016, 179}, - {0x8001, 209}, - {0xc016, 209}, - {0x8001, 216}, - {0xc016, 216}, - {0x8001, 217}, - {0xc016, 217}, - {0x8001, 227}, - {0xc016, 227}, - {0x8001, 229}, - {0xc016, 229}, - }, - /* 113 */ - { - {0x8002, 176}, - {0x8009, 176}, - {0x8017, 176}, - {0xc028, 176}, - {0x8002, 177}, - {0x8009, 177}, - {0x8017, 177}, - {0xc028, 177}, - {0x8002, 179}, - {0x8009, 179}, - {0x8017, 179}, - {0xc028, 179}, - {0x8002, 209}, - {0x8009, 209}, - {0x8017, 209}, - {0xc028, 209}, - }, - /* 114 */ - { - {0x8003, 176}, - {0x8006, 176}, - {0x800a, 176}, - {0x800f, 176}, - {0x8018, 176}, - {0x801f, 176}, - {0x8029, 176}, - {0xc038, 176}, - {0x8003, 177}, - {0x8006, 177}, - {0x800a, 177}, - {0x800f, 177}, - {0x8018, 177}, - {0x801f, 177}, - {0x8029, 177}, - {0xc038, 177}, - }, - /* 115 */ - { - {0x8003, 179}, - {0x8006, 179}, - {0x800a, 179}, - {0x800f, 179}, - {0x8018, 179}, - {0x801f, 179}, - {0x8029, 179}, - {0xc038, 179}, - {0x8003, 209}, - {0x8006, 209}, - {0x800a, 209}, - {0x800f, 209}, - {0x8018, 209}, - {0x801f, 209}, - {0x8029, 209}, - {0xc038, 209}, - }, - /* 116 */ - { - {0x8002, 216}, - {0x8009, 216}, - {0x8017, 216}, - {0xc028, 216}, - {0x8002, 217}, - {0x8009, 217}, - {0x8017, 217}, - {0xc028, 217}, - {0x8002, 227}, - {0x8009, 227}, - {0x8017, 227}, - {0xc028, 227}, - {0x8002, 229}, - {0x8009, 229}, - {0x8017, 229}, - {0xc028, 229}, - }, - /* 117 */ - { - {0x8003, 216}, - {0x8006, 216}, - {0x800a, 216}, - {0x800f, 216}, - {0x8018, 216}, - {0x801f, 216}, - {0x8029, 216}, - {0xc038, 216}, - {0x8003, 217}, - {0x8006, 217}, - {0x800a, 217}, - {0x800f, 217}, - {0x8018, 217}, - {0x801f, 217}, - {0x8029, 217}, - {0xc038, 217}, - }, - /* 118 */ - { - {0x8003, 227}, - {0x8006, 227}, - {0x800a, 227}, - {0x800f, 227}, - {0x8018, 227}, - {0x801f, 227}, - {0x8029, 227}, - {0xc038, 227}, - {0x8003, 229}, - {0x8006, 229}, - {0x800a, 229}, - {0x800f, 229}, - {0x8018, 229}, - {0x801f, 229}, - {0x8029, 229}, - {0xc038, 229}, - }, - /* 119 */ - { - {0x8001, 230}, - {0xc016, 230}, - {0xc000, 129}, - {0xc000, 132}, - {0xc000, 133}, - {0xc000, 134}, - {0xc000, 136}, - {0xc000, 146}, - {0xc000, 154}, - {0xc000, 156}, - {0xc000, 160}, - {0xc000, 163}, - {0xc000, 164}, - {0xc000, 169}, - {0xc000, 170}, - {0xc000, 173}, - }, - /* 120 */ - { - {0x8002, 230}, - {0x8009, 230}, - {0x8017, 230}, - {0xc028, 230}, - {0x8001, 129}, - {0xc016, 129}, - {0x8001, 132}, - {0xc016, 132}, - {0x8001, 133}, - {0xc016, 133}, - {0x8001, 134}, - {0xc016, 134}, - {0x8001, 136}, - {0xc016, 136}, - {0x8001, 146}, - {0xc016, 146}, - }, - /* 121 */ - { - {0x8003, 230}, - {0x8006, 230}, - {0x800a, 230}, - {0x800f, 230}, - {0x8018, 230}, - {0x801f, 230}, - {0x8029, 230}, - {0xc038, 230}, - {0x8002, 129}, - {0x8009, 129}, - {0x8017, 129}, - {0xc028, 129}, - {0x8002, 132}, - {0x8009, 132}, - {0x8017, 132}, - {0xc028, 132}, - }, - /* 122 */ - { - {0x8003, 129}, - {0x8006, 129}, - {0x800a, 129}, - {0x800f, 129}, - {0x8018, 129}, - {0x801f, 129}, - {0x8029, 129}, - {0xc038, 129}, - {0x8003, 132}, - {0x8006, 132}, - {0x800a, 132}, - {0x800f, 132}, - {0x8018, 132}, - {0x801f, 132}, - {0x8029, 132}, - {0xc038, 132}, - }, - /* 123 */ - { - {0x8002, 133}, - {0x8009, 133}, - {0x8017, 133}, - {0xc028, 133}, - {0x8002, 134}, - {0x8009, 134}, - {0x8017, 134}, - {0xc028, 134}, - {0x8002, 136}, - {0x8009, 136}, - {0x8017, 136}, - {0xc028, 136}, - {0x8002, 146}, - {0x8009, 146}, - {0x8017, 146}, - {0xc028, 146}, - }, - /* 124 */ - { - {0x8003, 133}, - {0x8006, 133}, - {0x800a, 133}, - {0x800f, 133}, - {0x8018, 133}, - {0x801f, 133}, - {0x8029, 133}, - {0xc038, 133}, - {0x8003, 134}, - {0x8006, 134}, - {0x800a, 134}, - {0x800f, 134}, - {0x8018, 134}, - {0x801f, 134}, - {0x8029, 134}, - {0xc038, 134}, - }, - /* 125 */ - { - {0x8003, 136}, - {0x8006, 136}, - {0x800a, 136}, - {0x800f, 136}, - {0x8018, 136}, - {0x801f, 136}, - {0x8029, 136}, - {0xc038, 136}, - {0x8003, 146}, - {0x8006, 146}, - {0x800a, 146}, - {0x800f, 146}, - {0x8018, 146}, - {0x801f, 146}, - {0x8029, 146}, - {0xc038, 146}, - }, - /* 126 */ - { - {0x8001, 154}, - {0xc016, 154}, - {0x8001, 156}, - {0xc016, 156}, - {0x8001, 160}, - {0xc016, 160}, - {0x8001, 163}, - {0xc016, 163}, - {0x8001, 164}, - {0xc016, 164}, - {0x8001, 169}, - {0xc016, 169}, - {0x8001, 170}, - {0xc016, 170}, - {0x8001, 173}, - {0xc016, 173}, - }, - /* 127 */ - { - {0x8002, 154}, - {0x8009, 154}, - {0x8017, 154}, - {0xc028, 154}, - {0x8002, 156}, - {0x8009, 156}, - {0x8017, 156}, - {0xc028, 156}, - {0x8002, 160}, - {0x8009, 160}, - {0x8017, 160}, - {0xc028, 160}, - {0x8002, 163}, - {0x8009, 163}, - {0x8017, 163}, - {0xc028, 163}, - }, - /* 128 */ - { - {0x8003, 154}, - {0x8006, 154}, - {0x800a, 154}, - {0x800f, 154}, - {0x8018, 154}, - {0x801f, 154}, - {0x8029, 154}, - {0xc038, 154}, - {0x8003, 156}, - {0x8006, 156}, - {0x800a, 156}, - {0x800f, 156}, - {0x8018, 156}, - {0x801f, 156}, - {0x8029, 156}, - {0xc038, 156}, - }, - /* 129 */ - { - {0x8003, 160}, - {0x8006, 160}, - {0x800a, 160}, - {0x800f, 160}, - {0x8018, 160}, - {0x801f, 160}, - {0x8029, 160}, - {0xc038, 160}, - {0x8003, 163}, - {0x8006, 163}, - {0x800a, 163}, - {0x800f, 163}, - {0x8018, 163}, - {0x801f, 163}, - {0x8029, 163}, - {0xc038, 163}, - }, - /* 130 */ - { - {0x8002, 164}, - {0x8009, 164}, - {0x8017, 164}, - {0xc028, 164}, - {0x8002, 169}, - {0x8009, 169}, - {0x8017, 169}, - {0xc028, 169}, - {0x8002, 170}, - {0x8009, 170}, - {0x8017, 170}, - {0xc028, 170}, - {0x8002, 173}, - {0x8009, 173}, - {0x8017, 173}, - {0xc028, 173}, - }, - /* 131 */ - { - {0x8003, 164}, - {0x8006, 164}, - {0x800a, 164}, - {0x800f, 164}, - {0x8018, 164}, - {0x801f, 164}, - {0x8029, 164}, - {0xc038, 164}, - {0x8003, 169}, - {0x8006, 169}, - {0x800a, 169}, - {0x800f, 169}, - {0x8018, 169}, - {0x801f, 169}, - {0x8029, 169}, - {0xc038, 169}, - }, - /* 132 */ - { - {0x8003, 170}, - {0x8006, 170}, - {0x800a, 170}, - {0x800f, 170}, - {0x8018, 170}, - {0x801f, 170}, - {0x8029, 170}, - {0xc038, 170}, - {0x8003, 173}, - {0x8006, 173}, - {0x800a, 173}, - {0x800f, 173}, - {0x8018, 173}, - {0x801f, 173}, - {0x8029, 173}, - {0xc038, 173}, - }, - /* 133 */ - { - {0x89, 0}, - {0x8a, 0}, - {0x8c, 0}, - {0x8d, 0}, - {0x90, 0}, - {0x91, 0}, - {0x93, 0}, - {0x96, 0}, - {0x9c, 0}, - {0x9f, 0}, - {0xa3, 0}, - {0xa6, 0}, - {0xab, 0}, - {0xae, 0}, - {0xb5, 0}, - {0xbe, 0}, - }, - /* 134 */ - { - {0xc000, 178}, - {0xc000, 181}, - {0xc000, 185}, - {0xc000, 186}, - {0xc000, 187}, - {0xc000, 189}, - {0xc000, 190}, - {0xc000, 196}, - {0xc000, 198}, - {0xc000, 228}, - {0xc000, 232}, - {0xc000, 233}, - {0x94, 0}, - {0x95, 0}, - {0x97, 0}, - {0x98, 0}, - }, - /* 135 */ - { - {0x8001, 178}, - {0xc016, 178}, - {0x8001, 181}, - {0xc016, 181}, - {0x8001, 185}, - {0xc016, 185}, - {0x8001, 186}, - {0xc016, 186}, - {0x8001, 187}, - {0xc016, 187}, - {0x8001, 189}, - {0xc016, 189}, - {0x8001, 190}, - {0xc016, 190}, - {0x8001, 196}, - {0xc016, 196}, - }, - /* 136 */ - { - {0x8002, 178}, - {0x8009, 178}, - {0x8017, 178}, - {0xc028, 178}, - {0x8002, 181}, - {0x8009, 181}, - {0x8017, 181}, - {0xc028, 181}, - {0x8002, 185}, - {0x8009, 185}, - {0x8017, 185}, - {0xc028, 185}, - {0x8002, 186}, - {0x8009, 186}, - {0x8017, 186}, - {0xc028, 186}, - }, - /* 137 */ - { - {0x8003, 178}, - {0x8006, 178}, - {0x800a, 178}, - {0x800f, 178}, - {0x8018, 178}, - {0x801f, 178}, - {0x8029, 178}, - {0xc038, 178}, - {0x8003, 181}, - {0x8006, 181}, - {0x800a, 181}, - {0x800f, 181}, - {0x8018, 181}, - {0x801f, 181}, - {0x8029, 181}, - {0xc038, 181}, - }, - /* 138 */ - { - {0x8003, 185}, - {0x8006, 185}, - {0x800a, 185}, - {0x800f, 185}, - {0x8018, 185}, - {0x801f, 185}, - {0x8029, 185}, - {0xc038, 185}, - {0x8003, 186}, - {0x8006, 186}, - {0x800a, 186}, - {0x800f, 186}, - {0x8018, 186}, - {0x801f, 186}, - {0x8029, 186}, - {0xc038, 186}, - }, - /* 139 */ - { - {0x8002, 187}, - {0x8009, 187}, - {0x8017, 187}, - {0xc028, 187}, - {0x8002, 189}, - {0x8009, 189}, - {0x8017, 189}, - {0xc028, 189}, - {0x8002, 190}, - {0x8009, 190}, - {0x8017, 190}, - {0xc028, 190}, - {0x8002, 196}, - {0x8009, 196}, - {0x8017, 196}, - {0xc028, 196}, - }, - /* 140 */ - { - {0x8003, 187}, - {0x8006, 187}, - {0x800a, 187}, - {0x800f, 187}, - {0x8018, 187}, - {0x801f, 187}, - {0x8029, 187}, - {0xc038, 187}, - {0x8003, 189}, - {0x8006, 189}, - {0x800a, 189}, - {0x800f, 189}, - {0x8018, 189}, - {0x801f, 189}, - {0x8029, 189}, - {0xc038, 189}, - }, - /* 141 */ - { - {0x8003, 190}, - {0x8006, 190}, - {0x800a, 190}, - {0x800f, 190}, - {0x8018, 190}, - {0x801f, 190}, - {0x8029, 190}, - {0xc038, 190}, - {0x8003, 196}, - {0x8006, 196}, - {0x800a, 196}, - {0x800f, 196}, - {0x8018, 196}, - {0x801f, 196}, - {0x8029, 196}, - {0xc038, 196}, - }, - /* 142 */ - { - {0x8001, 198}, - {0xc016, 198}, - {0x8001, 228}, - {0xc016, 228}, - {0x8001, 232}, - {0xc016, 232}, - {0x8001, 233}, - {0xc016, 233}, - {0xc000, 1}, - {0xc000, 135}, - {0xc000, 137}, - {0xc000, 138}, - {0xc000, 139}, - {0xc000, 140}, - {0xc000, 141}, - {0xc000, 143}, - }, - /* 143 */ - { - {0x8002, 198}, - {0x8009, 198}, - {0x8017, 198}, - {0xc028, 198}, - {0x8002, 228}, - {0x8009, 228}, - {0x8017, 228}, - {0xc028, 228}, - {0x8002, 232}, - {0x8009, 232}, - {0x8017, 232}, - {0xc028, 232}, - {0x8002, 233}, - {0x8009, 233}, - {0x8017, 233}, - {0xc028, 233}, - }, - /* 144 */ - { - {0x8003, 198}, - {0x8006, 198}, - {0x800a, 198}, - {0x800f, 198}, - {0x8018, 198}, - {0x801f, 198}, - {0x8029, 198}, - {0xc038, 198}, - {0x8003, 228}, - {0x8006, 228}, - {0x800a, 228}, - {0x800f, 228}, - {0x8018, 228}, - {0x801f, 228}, - {0x8029, 228}, - {0xc038, 228}, - }, - /* 145 */ - { - {0x8003, 232}, - {0x8006, 232}, - {0x800a, 232}, - {0x800f, 232}, - {0x8018, 232}, - {0x801f, 232}, - {0x8029, 232}, - {0xc038, 232}, - {0x8003, 233}, - {0x8006, 233}, - {0x800a, 233}, - {0x800f, 233}, - {0x8018, 233}, - {0x801f, 233}, - {0x8029, 233}, - {0xc038, 233}, - }, - /* 146 */ - { - {0x8001, 1}, - {0xc016, 1}, - {0x8001, 135}, - {0xc016, 135}, - {0x8001, 137}, - {0xc016, 137}, - {0x8001, 138}, - {0xc016, 138}, - {0x8001, 139}, - {0xc016, 139}, - {0x8001, 140}, - {0xc016, 140}, - {0x8001, 141}, - {0xc016, 141}, - {0x8001, 143}, - {0xc016, 143}, - }, - /* 147 */ - { - {0x8002, 1}, - {0x8009, 1}, - {0x8017, 1}, - {0xc028, 1}, - {0x8002, 135}, - {0x8009, 135}, - {0x8017, 135}, - {0xc028, 135}, - {0x8002, 137}, - {0x8009, 137}, - {0x8017, 137}, - {0xc028, 137}, - {0x8002, 138}, - {0x8009, 138}, - {0x8017, 138}, - {0xc028, 138}, - }, - /* 148 */ - { - {0x8003, 1}, - {0x8006, 1}, - {0x800a, 1}, - {0x800f, 1}, - {0x8018, 1}, - {0x801f, 1}, - {0x8029, 1}, - {0xc038, 1}, - {0x8003, 135}, - {0x8006, 135}, - {0x800a, 135}, - {0x800f, 135}, - {0x8018, 135}, - {0x801f, 135}, - {0x8029, 135}, - {0xc038, 135}, - }, - /* 149 */ - { - {0x8003, 137}, - {0x8006, 137}, - {0x800a, 137}, - {0x800f, 137}, - {0x8018, 137}, - {0x801f, 137}, - {0x8029, 137}, - {0xc038, 137}, - {0x8003, 138}, - {0x8006, 138}, - {0x800a, 138}, - {0x800f, 138}, - {0x8018, 138}, - {0x801f, 138}, - {0x8029, 138}, - {0xc038, 138}, - }, - /* 150 */ - { - {0x8002, 139}, - {0x8009, 139}, - {0x8017, 139}, - {0xc028, 139}, - {0x8002, 140}, - {0x8009, 140}, - {0x8017, 140}, - {0xc028, 140}, - {0x8002, 141}, - {0x8009, 141}, - {0x8017, 141}, - {0xc028, 141}, - {0x8002, 143}, - {0x8009, 143}, - {0x8017, 143}, - {0xc028, 143}, - }, - /* 151 */ - { - {0x8003, 139}, - {0x8006, 139}, - {0x800a, 139}, - {0x800f, 139}, - {0x8018, 139}, - {0x801f, 139}, - {0x8029, 139}, - {0xc038, 139}, - {0x8003, 140}, - {0x8006, 140}, - {0x800a, 140}, - {0x800f, 140}, - {0x8018, 140}, - {0x801f, 140}, - {0x8029, 140}, - {0xc038, 140}, - }, - /* 152 */ - { - {0x8003, 141}, - {0x8006, 141}, - {0x800a, 141}, - {0x800f, 141}, - {0x8018, 141}, - {0x801f, 141}, - {0x8029, 141}, - {0xc038, 141}, - {0x8003, 143}, - {0x8006, 143}, - {0x800a, 143}, - {0x800f, 143}, - {0x8018, 143}, - {0x801f, 143}, - {0x8029, 143}, - {0xc038, 143}, - }, - /* 153 */ - { - {0x9d, 0}, - {0x9e, 0}, - {0xa0, 0}, - {0xa1, 0}, - {0xa4, 0}, - {0xa5, 0}, - {0xa7, 0}, - {0xa8, 0}, - {0xac, 0}, - {0xad, 0}, - {0xaf, 0}, - {0xb1, 0}, - {0xb6, 0}, - {0xb9, 0}, - {0xbf, 0}, - {0xcf, 0}, - }, - /* 154 */ - { - {0xc000, 147}, - {0xc000, 149}, - {0xc000, 150}, - {0xc000, 151}, - {0xc000, 152}, - {0xc000, 155}, - {0xc000, 157}, - {0xc000, 158}, - {0xc000, 165}, - {0xc000, 166}, - {0xc000, 168}, - {0xc000, 174}, - {0xc000, 175}, - {0xc000, 180}, - {0xc000, 182}, - {0xc000, 183}, - }, - /* 155 */ - { - {0x8001, 147}, - {0xc016, 147}, - {0x8001, 149}, - {0xc016, 149}, - {0x8001, 150}, - {0xc016, 150}, - {0x8001, 151}, - {0xc016, 151}, - {0x8001, 152}, - {0xc016, 152}, - {0x8001, 155}, - {0xc016, 155}, - {0x8001, 157}, - {0xc016, 157}, - {0x8001, 158}, - {0xc016, 158}, - }, - /* 156 */ - { - {0x8002, 147}, - {0x8009, 147}, - {0x8017, 147}, - {0xc028, 147}, - {0x8002, 149}, - {0x8009, 149}, - {0x8017, 149}, - {0xc028, 149}, - {0x8002, 150}, - {0x8009, 150}, - {0x8017, 150}, - {0xc028, 150}, - {0x8002, 151}, - {0x8009, 151}, - {0x8017, 151}, - {0xc028, 151}, - }, - /* 157 */ - { - {0x8003, 147}, - {0x8006, 147}, - {0x800a, 147}, - {0x800f, 147}, - {0x8018, 147}, - {0x801f, 147}, - {0x8029, 147}, - {0xc038, 147}, - {0x8003, 149}, - {0x8006, 149}, - {0x800a, 149}, - {0x800f, 149}, - {0x8018, 149}, - {0x801f, 149}, - {0x8029, 149}, - {0xc038, 149}, - }, - /* 158 */ - { - {0x8003, 150}, - {0x8006, 150}, - {0x800a, 150}, - {0x800f, 150}, - {0x8018, 150}, - {0x801f, 150}, - {0x8029, 150}, - {0xc038, 150}, - {0x8003, 151}, - {0x8006, 151}, - {0x800a, 151}, - {0x800f, 151}, - {0x8018, 151}, - {0x801f, 151}, - {0x8029, 151}, - {0xc038, 151}, - }, - /* 159 */ - { - {0x8002, 152}, - {0x8009, 152}, - {0x8017, 152}, - {0xc028, 152}, - {0x8002, 155}, - {0x8009, 155}, - {0x8017, 155}, - {0xc028, 155}, - {0x8002, 157}, - {0x8009, 157}, - {0x8017, 157}, - {0xc028, 157}, - {0x8002, 158}, - {0x8009, 158}, - {0x8017, 158}, - {0xc028, 158}, - }, - /* 160 */ - { - {0x8003, 152}, - {0x8006, 152}, - {0x800a, 152}, - {0x800f, 152}, - {0x8018, 152}, - {0x801f, 152}, - {0x8029, 152}, - {0xc038, 152}, - {0x8003, 155}, - {0x8006, 155}, - {0x800a, 155}, - {0x800f, 155}, - {0x8018, 155}, - {0x801f, 155}, - {0x8029, 155}, - {0xc038, 155}, - }, - /* 161 */ - { - {0x8003, 157}, - {0x8006, 157}, - {0x800a, 157}, - {0x800f, 157}, - {0x8018, 157}, - {0x801f, 157}, - {0x8029, 157}, - {0xc038, 157}, - {0x8003, 158}, - {0x8006, 158}, - {0x800a, 158}, - {0x800f, 158}, - {0x8018, 158}, - {0x801f, 158}, - {0x8029, 158}, - {0xc038, 158}, - }, - /* 162 */ - { - {0x8001, 165}, - {0xc016, 165}, - {0x8001, 166}, - {0xc016, 166}, - {0x8001, 168}, - {0xc016, 168}, - {0x8001, 174}, - {0xc016, 174}, - {0x8001, 175}, - {0xc016, 175}, - {0x8001, 180}, - {0xc016, 180}, - {0x8001, 182}, - {0xc016, 182}, - {0x8001, 183}, - {0xc016, 183}, - }, - /* 163 */ - { - {0x8002, 165}, - {0x8009, 165}, - {0x8017, 165}, - {0xc028, 165}, - {0x8002, 166}, - {0x8009, 166}, - {0x8017, 166}, - {0xc028, 166}, - {0x8002, 168}, - {0x8009, 168}, - {0x8017, 168}, - {0xc028, 168}, - {0x8002, 174}, - {0x8009, 174}, - {0x8017, 174}, - {0xc028, 174}, - }, - /* 164 */ - { - {0x8003, 165}, - {0x8006, 165}, - {0x800a, 165}, - {0x800f, 165}, - {0x8018, 165}, - {0x801f, 165}, - {0x8029, 165}, - {0xc038, 165}, - {0x8003, 166}, - {0x8006, 166}, - {0x800a, 166}, - {0x800f, 166}, - {0x8018, 166}, - {0x801f, 166}, - {0x8029, 166}, - {0xc038, 166}, - }, - /* 165 */ - { - {0x8003, 168}, - {0x8006, 168}, - {0x800a, 168}, - {0x800f, 168}, - {0x8018, 168}, - {0x801f, 168}, - {0x8029, 168}, - {0xc038, 168}, - {0x8003, 174}, - {0x8006, 174}, - {0x800a, 174}, - {0x800f, 174}, - {0x8018, 174}, - {0x801f, 174}, - {0x8029, 174}, - {0xc038, 174}, - }, - /* 166 */ - { - {0x8002, 175}, - {0x8009, 175}, - {0x8017, 175}, - {0xc028, 175}, - {0x8002, 180}, - {0x8009, 180}, - {0x8017, 180}, - {0xc028, 180}, - {0x8002, 182}, - {0x8009, 182}, - {0x8017, 182}, - {0xc028, 182}, - {0x8002, 183}, - {0x8009, 183}, - {0x8017, 183}, - {0xc028, 183}, - }, - /* 167 */ - { - {0x8003, 175}, - {0x8006, 175}, - {0x800a, 175}, - {0x800f, 175}, - {0x8018, 175}, - {0x801f, 175}, - {0x8029, 175}, - {0xc038, 175}, - {0x8003, 180}, - {0x8006, 180}, - {0x800a, 180}, - {0x800f, 180}, - {0x8018, 180}, - {0x801f, 180}, - {0x8029, 180}, - {0xc038, 180}, - }, - /* 168 */ - { - {0x8003, 182}, - {0x8006, 182}, - {0x800a, 182}, - {0x800f, 182}, - {0x8018, 182}, - {0x801f, 182}, - {0x8029, 182}, - {0xc038, 182}, - {0x8003, 183}, - {0x8006, 183}, - {0x800a, 183}, - {0x800f, 183}, - {0x8018, 183}, - {0x801f, 183}, - {0x8029, 183}, - {0xc038, 183}, - }, - /* 169 */ - { - {0xc000, 188}, - {0xc000, 191}, - {0xc000, 197}, - {0xc000, 231}, - {0xc000, 239}, - {0xb0, 0}, - {0xb2, 0}, - {0xb3, 0}, - {0xb7, 0}, - {0xb8, 0}, - {0xba, 0}, - {0xbb, 0}, - {0xc0, 0}, - {0xc7, 0}, - {0xd0, 0}, - {0xdf, 0}, - }, - /* 170 */ - { - {0x8001, 188}, - {0xc016, 188}, - {0x8001, 191}, - {0xc016, 191}, - {0x8001, 197}, - {0xc016, 197}, - {0x8001, 231}, - {0xc016, 231}, - {0x8001, 239}, - {0xc016, 239}, - {0xc000, 9}, - {0xc000, 142}, - {0xc000, 144}, - {0xc000, 145}, - {0xc000, 148}, - {0xc000, 159}, - }, - /* 171 */ - { - {0x8002, 188}, - {0x8009, 188}, - {0x8017, 188}, - {0xc028, 188}, - {0x8002, 191}, - {0x8009, 191}, - {0x8017, 191}, - {0xc028, 191}, - {0x8002, 197}, - {0x8009, 197}, - {0x8017, 197}, - {0xc028, 197}, - {0x8002, 231}, - {0x8009, 231}, - {0x8017, 231}, - {0xc028, 231}, - }, - /* 172 */ - { - {0x8003, 188}, - {0x8006, 188}, - {0x800a, 188}, - {0x800f, 188}, - {0x8018, 188}, - {0x801f, 188}, - {0x8029, 188}, - {0xc038, 188}, - {0x8003, 191}, - {0x8006, 191}, - {0x800a, 191}, - {0x800f, 191}, - {0x8018, 191}, - {0x801f, 191}, - {0x8029, 191}, - {0xc038, 191}, - }, - /* 173 */ - { - {0x8003, 197}, - {0x8006, 197}, - {0x800a, 197}, - {0x800f, 197}, - {0x8018, 197}, - {0x801f, 197}, - {0x8029, 197}, - {0xc038, 197}, - {0x8003, 231}, - {0x8006, 231}, - {0x800a, 231}, - {0x800f, 231}, - {0x8018, 231}, - {0x801f, 231}, - {0x8029, 231}, - {0xc038, 231}, - }, - /* 174 */ - { - {0x8002, 239}, - {0x8009, 239}, - {0x8017, 239}, - {0xc028, 239}, - {0x8001, 9}, - {0xc016, 9}, - {0x8001, 142}, - {0xc016, 142}, - {0x8001, 144}, - {0xc016, 144}, - {0x8001, 145}, - {0xc016, 145}, - {0x8001, 148}, - {0xc016, 148}, - {0x8001, 159}, - {0xc016, 159}, - }, - /* 175 */ - { - {0x8003, 239}, - {0x8006, 239}, - {0x800a, 239}, - {0x800f, 239}, - {0x8018, 239}, - {0x801f, 239}, - {0x8029, 239}, - {0xc038, 239}, - {0x8002, 9}, - {0x8009, 9}, - {0x8017, 9}, - {0xc028, 9}, - {0x8002, 142}, - {0x8009, 142}, - {0x8017, 142}, - {0xc028, 142}, - }, - /* 176 */ - { - {0x8003, 9}, - {0x8006, 9}, - {0x800a, 9}, - {0x800f, 9}, - {0x8018, 9}, - {0x801f, 9}, - {0x8029, 9}, - {0xc038, 9}, - {0x8003, 142}, - {0x8006, 142}, - {0x800a, 142}, - {0x800f, 142}, - {0x8018, 142}, - {0x801f, 142}, - {0x8029, 142}, - {0xc038, 142}, - }, - /* 177 */ - { - {0x8002, 144}, - {0x8009, 144}, - {0x8017, 144}, - {0xc028, 144}, - {0x8002, 145}, - {0x8009, 145}, - {0x8017, 145}, - {0xc028, 145}, - {0x8002, 148}, - {0x8009, 148}, - {0x8017, 148}, - {0xc028, 148}, - {0x8002, 159}, - {0x8009, 159}, - {0x8017, 159}, - {0xc028, 159}, - }, - /* 178 */ - { - {0x8003, 144}, - {0x8006, 144}, - {0x800a, 144}, - {0x800f, 144}, - {0x8018, 144}, - {0x801f, 144}, - {0x8029, 144}, - {0xc038, 144}, - {0x8003, 145}, - {0x8006, 145}, - {0x800a, 145}, - {0x800f, 145}, - {0x8018, 145}, - {0x801f, 145}, - {0x8029, 145}, - {0xc038, 145}, - }, - /* 179 */ - { - {0x8003, 148}, - {0x8006, 148}, - {0x800a, 148}, - {0x800f, 148}, - {0x8018, 148}, - {0x801f, 148}, - {0x8029, 148}, - {0xc038, 148}, - {0x8003, 159}, - {0x8006, 159}, - {0x800a, 159}, - {0x800f, 159}, - {0x8018, 159}, - {0x801f, 159}, - {0x8029, 159}, - {0xc038, 159}, - }, - /* 180 */ - { - {0xc000, 171}, - {0xc000, 206}, - {0xc000, 215}, - {0xc000, 225}, - {0xc000, 236}, - {0xc000, 237}, - {0xbc, 0}, - {0xbd, 0}, - {0xc1, 0}, - {0xc4, 0}, - {0xc8, 0}, - {0xcb, 0}, - {0xd1, 0}, - {0xd8, 0}, - {0xe0, 0}, - {0xee, 0}, - }, - /* 181 */ - { - {0x8001, 171}, - {0xc016, 171}, - {0x8001, 206}, - {0xc016, 206}, - {0x8001, 215}, - {0xc016, 215}, - {0x8001, 225}, - {0xc016, 225}, - {0x8001, 236}, - {0xc016, 236}, - {0x8001, 237}, - {0xc016, 237}, - {0xc000, 199}, - {0xc000, 207}, - {0xc000, 234}, - {0xc000, 235}, - }, - /* 182 */ - { - {0x8002, 171}, - {0x8009, 171}, - {0x8017, 171}, - {0xc028, 171}, - {0x8002, 206}, - {0x8009, 206}, - {0x8017, 206}, - {0xc028, 206}, - {0x8002, 215}, - {0x8009, 215}, - {0x8017, 215}, - {0xc028, 215}, - {0x8002, 225}, - {0x8009, 225}, - {0x8017, 225}, - {0xc028, 225}, - }, - /* 183 */ - { - {0x8003, 171}, - {0x8006, 171}, - {0x800a, 171}, - {0x800f, 171}, - {0x8018, 171}, - {0x801f, 171}, - {0x8029, 171}, - {0xc038, 171}, - {0x8003, 206}, - {0x8006, 206}, - {0x800a, 206}, - {0x800f, 206}, - {0x8018, 206}, - {0x801f, 206}, - {0x8029, 206}, - {0xc038, 206}, - }, - /* 184 */ - { - {0x8003, 215}, - {0x8006, 215}, - {0x800a, 215}, - {0x800f, 215}, - {0x8018, 215}, - {0x801f, 215}, - {0x8029, 215}, - {0xc038, 215}, - {0x8003, 225}, - {0x8006, 225}, - {0x800a, 225}, - {0x800f, 225}, - {0x8018, 225}, - {0x801f, 225}, - {0x8029, 225}, - {0xc038, 225}, - }, - /* 185 */ - { - {0x8002, 236}, - {0x8009, 236}, - {0x8017, 236}, - {0xc028, 236}, - {0x8002, 237}, - {0x8009, 237}, - {0x8017, 237}, - {0xc028, 237}, - {0x8001, 199}, - {0xc016, 199}, - {0x8001, 207}, - {0xc016, 207}, - {0x8001, 234}, - {0xc016, 234}, - {0x8001, 235}, - {0xc016, 235}, - }, - /* 186 */ - { - {0x8003, 236}, - {0x8006, 236}, - {0x800a, 236}, - {0x800f, 236}, - {0x8018, 236}, - {0x801f, 236}, - {0x8029, 236}, - {0xc038, 236}, - {0x8003, 237}, - {0x8006, 237}, - {0x800a, 237}, - {0x800f, 237}, - {0x8018, 237}, - {0x801f, 237}, - {0x8029, 237}, - {0xc038, 237}, - }, - /* 187 */ - { - {0x8002, 199}, - {0x8009, 199}, - {0x8017, 199}, - {0xc028, 199}, - {0x8002, 207}, - {0x8009, 207}, - {0x8017, 207}, - {0xc028, 207}, - {0x8002, 234}, - {0x8009, 234}, - {0x8017, 234}, - {0xc028, 234}, - {0x8002, 235}, - {0x8009, 235}, - {0x8017, 235}, - {0xc028, 235}, - }, - /* 188 */ - { - {0x8003, 199}, - {0x8006, 199}, - {0x800a, 199}, - {0x800f, 199}, - {0x8018, 199}, - {0x801f, 199}, - {0x8029, 199}, - {0xc038, 199}, - {0x8003, 207}, - {0x8006, 207}, - {0x800a, 207}, - {0x800f, 207}, - {0x8018, 207}, - {0x801f, 207}, - {0x8029, 207}, - {0xc038, 207}, - }, - /* 189 */ - { - {0x8003, 234}, - {0x8006, 234}, - {0x800a, 234}, - {0x800f, 234}, - {0x8018, 234}, - {0x801f, 234}, - {0x8029, 234}, - {0xc038, 234}, - {0x8003, 235}, - {0x8006, 235}, - {0x800a, 235}, - {0x800f, 235}, - {0x8018, 235}, - {0x801f, 235}, - {0x8029, 235}, - {0xc038, 235}, - }, - /* 190 */ - { - {0xc2, 0}, - {0xc3, 0}, - {0xc5, 0}, - {0xc6, 0}, - {0xc9, 0}, - {0xca, 0}, - {0xcc, 0}, - {0xcd, 0}, - {0xd2, 0}, - {0xd5, 0}, - {0xd9, 0}, - {0xdc, 0}, - {0xe1, 0}, - {0xe7, 0}, - {0xef, 0}, - {0xf6, 0}, - }, - /* 191 */ - { - {0xc000, 192}, - {0xc000, 193}, - {0xc000, 200}, - {0xc000, 201}, - {0xc000, 202}, - {0xc000, 205}, - {0xc000, 210}, - {0xc000, 213}, - {0xc000, 218}, - {0xc000, 219}, - {0xc000, 238}, - {0xc000, 240}, - {0xc000, 242}, - {0xc000, 243}, - {0xc000, 255}, - {0xce, 0}, - }, - /* 192 */ - { - {0x8001, 192}, - {0xc016, 192}, - {0x8001, 193}, - {0xc016, 193}, - {0x8001, 200}, - {0xc016, 200}, - {0x8001, 201}, - {0xc016, 201}, - {0x8001, 202}, - {0xc016, 202}, - {0x8001, 205}, - {0xc016, 205}, - {0x8001, 210}, - {0xc016, 210}, - {0x8001, 213}, - {0xc016, 213}, - }, - /* 193 */ - { - {0x8002, 192}, - {0x8009, 192}, - {0x8017, 192}, - {0xc028, 192}, - {0x8002, 193}, - {0x8009, 193}, - {0x8017, 193}, - {0xc028, 193}, - {0x8002, 200}, - {0x8009, 200}, - {0x8017, 200}, - {0xc028, 200}, - {0x8002, 201}, - {0x8009, 201}, - {0x8017, 201}, - {0xc028, 201}, - }, - /* 194 */ - { - {0x8003, 192}, - {0x8006, 192}, - {0x800a, 192}, - {0x800f, 192}, - {0x8018, 192}, - {0x801f, 192}, - {0x8029, 192}, - {0xc038, 192}, - {0x8003, 193}, - {0x8006, 193}, - {0x800a, 193}, - {0x800f, 193}, - {0x8018, 193}, - {0x801f, 193}, - {0x8029, 193}, - {0xc038, 193}, - }, - /* 195 */ - { - {0x8003, 200}, - {0x8006, 200}, - {0x800a, 200}, - {0x800f, 200}, - {0x8018, 200}, - {0x801f, 200}, - {0x8029, 200}, - {0xc038, 200}, - {0x8003, 201}, - {0x8006, 201}, - {0x800a, 201}, - {0x800f, 201}, - {0x8018, 201}, - {0x801f, 201}, - {0x8029, 201}, - {0xc038, 201}, - }, - /* 196 */ - { - {0x8002, 202}, - {0x8009, 202}, - {0x8017, 202}, - {0xc028, 202}, - {0x8002, 205}, - {0x8009, 205}, - {0x8017, 205}, - {0xc028, 205}, - {0x8002, 210}, - {0x8009, 210}, - {0x8017, 210}, - {0xc028, 210}, - {0x8002, 213}, - {0x8009, 213}, - {0x8017, 213}, - {0xc028, 213}, - }, - /* 197 */ - { - {0x8003, 202}, - {0x8006, 202}, - {0x800a, 202}, - {0x800f, 202}, - {0x8018, 202}, - {0x801f, 202}, - {0x8029, 202}, - {0xc038, 202}, - {0x8003, 205}, - {0x8006, 205}, - {0x800a, 205}, - {0x800f, 205}, - {0x8018, 205}, - {0x801f, 205}, - {0x8029, 205}, - {0xc038, 205}, - }, - /* 198 */ - { - {0x8003, 210}, - {0x8006, 210}, - {0x800a, 210}, - {0x800f, 210}, - {0x8018, 210}, - {0x801f, 210}, - {0x8029, 210}, - {0xc038, 210}, - {0x8003, 213}, - {0x8006, 213}, - {0x800a, 213}, - {0x800f, 213}, - {0x8018, 213}, - {0x801f, 213}, - {0x8029, 213}, - {0xc038, 213}, - }, - /* 199 */ - { - {0x8001, 218}, - {0xc016, 218}, - {0x8001, 219}, - {0xc016, 219}, - {0x8001, 238}, - {0xc016, 238}, - {0x8001, 240}, - {0xc016, 240}, - {0x8001, 242}, - {0xc016, 242}, - {0x8001, 243}, - {0xc016, 243}, - {0x8001, 255}, - {0xc016, 255}, - {0xc000, 203}, - {0xc000, 204}, - }, - /* 200 */ - { - {0x8002, 218}, - {0x8009, 218}, - {0x8017, 218}, - {0xc028, 218}, - {0x8002, 219}, - {0x8009, 219}, - {0x8017, 219}, - {0xc028, 219}, - {0x8002, 238}, - {0x8009, 238}, - {0x8017, 238}, - {0xc028, 238}, - {0x8002, 240}, - {0x8009, 240}, - {0x8017, 240}, - {0xc028, 240}, - }, - /* 201 */ - { - {0x8003, 218}, - {0x8006, 218}, - {0x800a, 218}, - {0x800f, 218}, - {0x8018, 218}, - {0x801f, 218}, - {0x8029, 218}, - {0xc038, 218}, - {0x8003, 219}, - {0x8006, 219}, - {0x800a, 219}, - {0x800f, 219}, - {0x8018, 219}, - {0x801f, 219}, - {0x8029, 219}, - {0xc038, 219}, - }, - /* 202 */ - { - {0x8003, 238}, - {0x8006, 238}, - {0x800a, 238}, - {0x800f, 238}, - {0x8018, 238}, - {0x801f, 238}, - {0x8029, 238}, - {0xc038, 238}, - {0x8003, 240}, - {0x8006, 240}, - {0x800a, 240}, - {0x800f, 240}, - {0x8018, 240}, - {0x801f, 240}, - {0x8029, 240}, - {0xc038, 240}, - }, - /* 203 */ - { - {0x8002, 242}, - {0x8009, 242}, - {0x8017, 242}, - {0xc028, 242}, - {0x8002, 243}, - {0x8009, 243}, - {0x8017, 243}, - {0xc028, 243}, - {0x8002, 255}, - {0x8009, 255}, - {0x8017, 255}, - {0xc028, 255}, - {0x8001, 203}, - {0xc016, 203}, - {0x8001, 204}, - {0xc016, 204}, - }, - /* 204 */ - { - {0x8003, 242}, - {0x8006, 242}, - {0x800a, 242}, - {0x800f, 242}, - {0x8018, 242}, - {0x801f, 242}, - {0x8029, 242}, - {0xc038, 242}, - {0x8003, 243}, - {0x8006, 243}, - {0x800a, 243}, - {0x800f, 243}, - {0x8018, 243}, - {0x801f, 243}, - {0x8029, 243}, - {0xc038, 243}, - }, - /* 205 */ - { - {0x8003, 255}, - {0x8006, 255}, - {0x800a, 255}, - {0x800f, 255}, - {0x8018, 255}, - {0x801f, 255}, - {0x8029, 255}, - {0xc038, 255}, - {0x8002, 203}, - {0x8009, 203}, - {0x8017, 203}, - {0xc028, 203}, - {0x8002, 204}, - {0x8009, 204}, - {0x8017, 204}, - {0xc028, 204}, - }, - /* 206 */ - { - {0x8003, 203}, - {0x8006, 203}, - {0x800a, 203}, - {0x800f, 203}, - {0x8018, 203}, - {0x801f, 203}, - {0x8029, 203}, - {0xc038, 203}, - {0x8003, 204}, - {0x8006, 204}, - {0x800a, 204}, - {0x800f, 204}, - {0x8018, 204}, - {0x801f, 204}, - {0x8029, 204}, - {0xc038, 204}, - }, - /* 207 */ - { - {0xd3, 0}, - {0xd4, 0}, - {0xd6, 0}, - {0xd7, 0}, - {0xda, 0}, - {0xdb, 0}, - {0xdd, 0}, - {0xde, 0}, - {0xe2, 0}, - {0xe4, 0}, - {0xe8, 0}, - {0xeb, 0}, - {0xf0, 0}, - {0xf3, 0}, - {0xf7, 0}, - {0xfa, 0}, - }, - /* 208 */ - { - {0xc000, 211}, - {0xc000, 212}, - {0xc000, 214}, - {0xc000, 221}, - {0xc000, 222}, - {0xc000, 223}, - {0xc000, 241}, - {0xc000, 244}, - {0xc000, 245}, - {0xc000, 246}, - {0xc000, 247}, - {0xc000, 248}, - {0xc000, 250}, - {0xc000, 251}, - {0xc000, 252}, - {0xc000, 253}, - }, - /* 209 */ - { - {0x8001, 211}, - {0xc016, 211}, - {0x8001, 212}, - {0xc016, 212}, - {0x8001, 214}, - {0xc016, 214}, - {0x8001, 221}, - {0xc016, 221}, - {0x8001, 222}, - {0xc016, 222}, - {0x8001, 223}, - {0xc016, 223}, - {0x8001, 241}, - {0xc016, 241}, - {0x8001, 244}, - {0xc016, 244}, - }, - /* 210 */ - { - {0x8002, 211}, - {0x8009, 211}, - {0x8017, 211}, - {0xc028, 211}, - {0x8002, 212}, - {0x8009, 212}, - {0x8017, 212}, - {0xc028, 212}, - {0x8002, 214}, - {0x8009, 214}, - {0x8017, 214}, - {0xc028, 214}, - {0x8002, 221}, - {0x8009, 221}, - {0x8017, 221}, - {0xc028, 221}, - }, - /* 211 */ - { - {0x8003, 211}, - {0x8006, 211}, - {0x800a, 211}, - {0x800f, 211}, - {0x8018, 211}, - {0x801f, 211}, - {0x8029, 211}, - {0xc038, 211}, - {0x8003, 212}, - {0x8006, 212}, - {0x800a, 212}, - {0x800f, 212}, - {0x8018, 212}, - {0x801f, 212}, - {0x8029, 212}, - {0xc038, 212}, - }, - /* 212 */ - { - {0x8003, 214}, - {0x8006, 214}, - {0x800a, 214}, - {0x800f, 214}, - {0x8018, 214}, - {0x801f, 214}, - {0x8029, 214}, - {0xc038, 214}, - {0x8003, 221}, - {0x8006, 221}, - {0x800a, 221}, - {0x800f, 221}, - {0x8018, 221}, - {0x801f, 221}, - {0x8029, 221}, - {0xc038, 221}, - }, - /* 213 */ - { - {0x8002, 222}, - {0x8009, 222}, - {0x8017, 222}, - {0xc028, 222}, - {0x8002, 223}, - {0x8009, 223}, - {0x8017, 223}, - {0xc028, 223}, - {0x8002, 241}, - {0x8009, 241}, - {0x8017, 241}, - {0xc028, 241}, - {0x8002, 244}, - {0x8009, 244}, - {0x8017, 244}, - {0xc028, 244}, - }, - /* 214 */ - { - {0x8003, 222}, - {0x8006, 222}, - {0x800a, 222}, - {0x800f, 222}, - {0x8018, 222}, - {0x801f, 222}, - {0x8029, 222}, - {0xc038, 222}, - {0x8003, 223}, - {0x8006, 223}, - {0x800a, 223}, - {0x800f, 223}, - {0x8018, 223}, - {0x801f, 223}, - {0x8029, 223}, - {0xc038, 223}, - }, - /* 215 */ - { - {0x8003, 241}, - {0x8006, 241}, - {0x800a, 241}, - {0x800f, 241}, - {0x8018, 241}, - {0x801f, 241}, - {0x8029, 241}, - {0xc038, 241}, - {0x8003, 244}, - {0x8006, 244}, - {0x800a, 244}, - {0x800f, 244}, - {0x8018, 244}, - {0x801f, 244}, - {0x8029, 244}, - {0xc038, 244}, - }, - /* 216 */ - { - {0x8001, 245}, - {0xc016, 245}, - {0x8001, 246}, - {0xc016, 246}, - {0x8001, 247}, - {0xc016, 247}, - {0x8001, 248}, - {0xc016, 248}, - {0x8001, 250}, - {0xc016, 250}, - {0x8001, 251}, - {0xc016, 251}, - {0x8001, 252}, - {0xc016, 252}, - {0x8001, 253}, - {0xc016, 253}, - }, - /* 217 */ - { - {0x8002, 245}, - {0x8009, 245}, - {0x8017, 245}, - {0xc028, 245}, - {0x8002, 246}, - {0x8009, 246}, - {0x8017, 246}, - {0xc028, 246}, - {0x8002, 247}, - {0x8009, 247}, - {0x8017, 247}, - {0xc028, 247}, - {0x8002, 248}, - {0x8009, 248}, - {0x8017, 248}, - {0xc028, 248}, - }, - /* 218 */ - { - {0x8003, 245}, - {0x8006, 245}, - {0x800a, 245}, - {0x800f, 245}, - {0x8018, 245}, - {0x801f, 245}, - {0x8029, 245}, - {0xc038, 245}, - {0x8003, 246}, - {0x8006, 246}, - {0x800a, 246}, - {0x800f, 246}, - {0x8018, 246}, - {0x801f, 246}, - {0x8029, 246}, - {0xc038, 246}, - }, - /* 219 */ - { - {0x8003, 247}, - {0x8006, 247}, - {0x800a, 247}, - {0x800f, 247}, - {0x8018, 247}, - {0x801f, 247}, - {0x8029, 247}, - {0xc038, 247}, - {0x8003, 248}, - {0x8006, 248}, - {0x800a, 248}, - {0x800f, 248}, - {0x8018, 248}, - {0x801f, 248}, - {0x8029, 248}, - {0xc038, 248}, - }, - /* 220 */ - { - {0x8002, 250}, - {0x8009, 250}, - {0x8017, 250}, - {0xc028, 250}, - {0x8002, 251}, - {0x8009, 251}, - {0x8017, 251}, - {0xc028, 251}, - {0x8002, 252}, - {0x8009, 252}, - {0x8017, 252}, - {0xc028, 252}, - {0x8002, 253}, - {0x8009, 253}, - {0x8017, 253}, - {0xc028, 253}, - }, - /* 221 */ - { - {0x8003, 250}, - {0x8006, 250}, - {0x800a, 250}, - {0x800f, 250}, - {0x8018, 250}, - {0x801f, 250}, - {0x8029, 250}, - {0xc038, 250}, - {0x8003, 251}, - {0x8006, 251}, - {0x800a, 251}, - {0x800f, 251}, - {0x8018, 251}, - {0x801f, 251}, - {0x8029, 251}, - {0xc038, 251}, - }, - /* 222 */ - { - {0x8003, 252}, - {0x8006, 252}, - {0x800a, 252}, - {0x800f, 252}, - {0x8018, 252}, - {0x801f, 252}, - {0x8029, 252}, - {0xc038, 252}, - {0x8003, 253}, - {0x8006, 253}, - {0x800a, 253}, - {0x800f, 253}, - {0x8018, 253}, - {0x801f, 253}, - {0x8029, 253}, - {0xc038, 253}, - }, - /* 223 */ - { - {0xc000, 254}, - {0xe3, 0}, - {0xe5, 0}, - {0xe6, 0}, - {0xe9, 0}, - {0xea, 0}, - {0xec, 0}, - {0xed, 0}, - {0xf1, 0}, - {0xf2, 0}, - {0xf4, 0}, - {0xf5, 0}, - {0xf8, 0}, - {0xf9, 0}, - {0xfb, 0}, - {0xfc, 0}, - }, - /* 224 */ - { - {0x8001, 254}, - {0xc016, 254}, - {0xc000, 2}, - {0xc000, 3}, - {0xc000, 4}, - {0xc000, 5}, - {0xc000, 6}, - {0xc000, 7}, - {0xc000, 8}, - {0xc000, 11}, - {0xc000, 12}, - {0xc000, 14}, - {0xc000, 15}, - {0xc000, 16}, - {0xc000, 17}, - {0xc000, 18}, - }, - /* 225 */ - { - {0x8002, 254}, - {0x8009, 254}, - {0x8017, 254}, - {0xc028, 254}, - {0x8001, 2}, - {0xc016, 2}, - {0x8001, 3}, - {0xc016, 3}, - {0x8001, 4}, - {0xc016, 4}, - {0x8001, 5}, - {0xc016, 5}, - {0x8001, 6}, - {0xc016, 6}, - {0x8001, 7}, - {0xc016, 7}, - }, - /* 226 */ - { - {0x8003, 254}, - {0x8006, 254}, - {0x800a, 254}, - {0x800f, 254}, - {0x8018, 254}, - {0x801f, 254}, - {0x8029, 254}, - {0xc038, 254}, - {0x8002, 2}, - {0x8009, 2}, - {0x8017, 2}, - {0xc028, 2}, - {0x8002, 3}, - {0x8009, 3}, - {0x8017, 3}, - {0xc028, 3}, - }, - /* 227 */ - { - {0x8003, 2}, - {0x8006, 2}, - {0x800a, 2}, - {0x800f, 2}, - {0x8018, 2}, - {0x801f, 2}, - {0x8029, 2}, - {0xc038, 2}, - {0x8003, 3}, - {0x8006, 3}, - {0x800a, 3}, - {0x800f, 3}, - {0x8018, 3}, - {0x801f, 3}, - {0x8029, 3}, - {0xc038, 3}, - }, - /* 228 */ - { - {0x8002, 4}, - {0x8009, 4}, - {0x8017, 4}, - {0xc028, 4}, - {0x8002, 5}, - {0x8009, 5}, - {0x8017, 5}, - {0xc028, 5}, - {0x8002, 6}, - {0x8009, 6}, - {0x8017, 6}, - {0xc028, 6}, - {0x8002, 7}, - {0x8009, 7}, - {0x8017, 7}, - {0xc028, 7}, - }, - /* 229 */ - { - {0x8003, 4}, - {0x8006, 4}, - {0x800a, 4}, - {0x800f, 4}, - {0x8018, 4}, - {0x801f, 4}, - {0x8029, 4}, - {0xc038, 4}, - {0x8003, 5}, - {0x8006, 5}, - {0x800a, 5}, - {0x800f, 5}, - {0x8018, 5}, - {0x801f, 5}, - {0x8029, 5}, - {0xc038, 5}, - }, - /* 230 */ - { - {0x8003, 6}, - {0x8006, 6}, - {0x800a, 6}, - {0x800f, 6}, - {0x8018, 6}, - {0x801f, 6}, - {0x8029, 6}, - {0xc038, 6}, - {0x8003, 7}, - {0x8006, 7}, - {0x800a, 7}, - {0x800f, 7}, - {0x8018, 7}, - {0x801f, 7}, - {0x8029, 7}, - {0xc038, 7}, - }, - /* 231 */ - { - {0x8001, 8}, - {0xc016, 8}, - {0x8001, 11}, - {0xc016, 11}, - {0x8001, 12}, - {0xc016, 12}, - {0x8001, 14}, - {0xc016, 14}, - {0x8001, 15}, - {0xc016, 15}, - {0x8001, 16}, - {0xc016, 16}, - {0x8001, 17}, - {0xc016, 17}, - {0x8001, 18}, - {0xc016, 18}, - }, - /* 232 */ - { - {0x8002, 8}, - {0x8009, 8}, - {0x8017, 8}, - {0xc028, 8}, - {0x8002, 11}, - {0x8009, 11}, - {0x8017, 11}, - {0xc028, 11}, - {0x8002, 12}, - {0x8009, 12}, - {0x8017, 12}, - {0xc028, 12}, - {0x8002, 14}, - {0x8009, 14}, - {0x8017, 14}, - {0xc028, 14}, - }, - /* 233 */ - { - {0x8003, 8}, - {0x8006, 8}, - {0x800a, 8}, - {0x800f, 8}, - {0x8018, 8}, - {0x801f, 8}, - {0x8029, 8}, - {0xc038, 8}, - {0x8003, 11}, - {0x8006, 11}, - {0x800a, 11}, - {0x800f, 11}, - {0x8018, 11}, - {0x801f, 11}, - {0x8029, 11}, - {0xc038, 11}, - }, - /* 234 */ - { - {0x8003, 12}, - {0x8006, 12}, - {0x800a, 12}, - {0x800f, 12}, - {0x8018, 12}, - {0x801f, 12}, - {0x8029, 12}, - {0xc038, 12}, - {0x8003, 14}, - {0x8006, 14}, - {0x800a, 14}, - {0x800f, 14}, - {0x8018, 14}, - {0x801f, 14}, - {0x8029, 14}, - {0xc038, 14}, - }, - /* 235 */ - { - {0x8002, 15}, - {0x8009, 15}, - {0x8017, 15}, - {0xc028, 15}, - {0x8002, 16}, - {0x8009, 16}, - {0x8017, 16}, - {0xc028, 16}, - {0x8002, 17}, - {0x8009, 17}, - {0x8017, 17}, - {0xc028, 17}, - {0x8002, 18}, - {0x8009, 18}, - {0x8017, 18}, - {0xc028, 18}, - }, - /* 236 */ - { - {0x8003, 15}, - {0x8006, 15}, - {0x800a, 15}, - {0x800f, 15}, - {0x8018, 15}, - {0x801f, 15}, - {0x8029, 15}, - {0xc038, 15}, - {0x8003, 16}, - {0x8006, 16}, - {0x800a, 16}, - {0x800f, 16}, - {0x8018, 16}, - {0x801f, 16}, - {0x8029, 16}, - {0xc038, 16}, - }, - /* 237 */ - { - {0x8003, 17}, - {0x8006, 17}, - {0x800a, 17}, - {0x800f, 17}, - {0x8018, 17}, - {0x801f, 17}, - {0x8029, 17}, - {0xc038, 17}, - {0x8003, 18}, - {0x8006, 18}, - {0x800a, 18}, - {0x800f, 18}, - {0x8018, 18}, - {0x801f, 18}, - {0x8029, 18}, - {0xc038, 18}, - }, - /* 238 */ - { - {0xc000, 19}, - {0xc000, 20}, - {0xc000, 21}, - {0xc000, 23}, - {0xc000, 24}, - {0xc000, 25}, - {0xc000, 26}, - {0xc000, 27}, - {0xc000, 28}, - {0xc000, 29}, - {0xc000, 30}, - {0xc000, 31}, - {0xc000, 127}, - {0xc000, 220}, - {0xc000, 249}, - {0xfd, 0}, - }, - /* 239 */ - { - {0x8001, 19}, - {0xc016, 19}, - {0x8001, 20}, - {0xc016, 20}, - {0x8001, 21}, - {0xc016, 21}, - {0x8001, 23}, - {0xc016, 23}, - {0x8001, 24}, - {0xc016, 24}, - {0x8001, 25}, - {0xc016, 25}, - {0x8001, 26}, - {0xc016, 26}, - {0x8001, 27}, - {0xc016, 27}, - }, - /* 240 */ - { - {0x8002, 19}, - {0x8009, 19}, - {0x8017, 19}, - {0xc028, 19}, - {0x8002, 20}, - {0x8009, 20}, - {0x8017, 20}, - {0xc028, 20}, - {0x8002, 21}, - {0x8009, 21}, - {0x8017, 21}, - {0xc028, 21}, - {0x8002, 23}, - {0x8009, 23}, - {0x8017, 23}, - {0xc028, 23}, - }, - /* 241 */ - { - {0x8003, 19}, - {0x8006, 19}, - {0x800a, 19}, - {0x800f, 19}, - {0x8018, 19}, - {0x801f, 19}, - {0x8029, 19}, - {0xc038, 19}, - {0x8003, 20}, - {0x8006, 20}, - {0x800a, 20}, - {0x800f, 20}, - {0x8018, 20}, - {0x801f, 20}, - {0x8029, 20}, - {0xc038, 20}, - }, - /* 242 */ - { - {0x8003, 21}, - {0x8006, 21}, - {0x800a, 21}, - {0x800f, 21}, - {0x8018, 21}, - {0x801f, 21}, - {0x8029, 21}, - {0xc038, 21}, - {0x8003, 23}, - {0x8006, 23}, - {0x800a, 23}, - {0x800f, 23}, - {0x8018, 23}, - {0x801f, 23}, - {0x8029, 23}, - {0xc038, 23}, - }, - /* 243 */ - { - {0x8002, 24}, - {0x8009, 24}, - {0x8017, 24}, - {0xc028, 24}, - {0x8002, 25}, - {0x8009, 25}, - {0x8017, 25}, - {0xc028, 25}, - {0x8002, 26}, - {0x8009, 26}, - {0x8017, 26}, - {0xc028, 26}, - {0x8002, 27}, - {0x8009, 27}, - {0x8017, 27}, - {0xc028, 27}, - }, - /* 244 */ - { - {0x8003, 24}, - {0x8006, 24}, - {0x800a, 24}, - {0x800f, 24}, - {0x8018, 24}, - {0x801f, 24}, - {0x8029, 24}, - {0xc038, 24}, - {0x8003, 25}, - {0x8006, 25}, - {0x800a, 25}, - {0x800f, 25}, - {0x8018, 25}, - {0x801f, 25}, - {0x8029, 25}, - {0xc038, 25}, - }, - /* 245 */ - { - {0x8003, 26}, - {0x8006, 26}, - {0x800a, 26}, - {0x800f, 26}, - {0x8018, 26}, - {0x801f, 26}, - {0x8029, 26}, - {0xc038, 26}, - {0x8003, 27}, - {0x8006, 27}, - {0x800a, 27}, - {0x800f, 27}, - {0x8018, 27}, - {0x801f, 27}, - {0x8029, 27}, - {0xc038, 27}, - }, - /* 246 */ - { - {0x8001, 28}, - {0xc016, 28}, - {0x8001, 29}, - {0xc016, 29}, - {0x8001, 30}, - {0xc016, 30}, - {0x8001, 31}, - {0xc016, 31}, - {0x8001, 127}, - {0xc016, 127}, - {0x8001, 220}, - {0xc016, 220}, - {0x8001, 249}, - {0xc016, 249}, - {0xfe, 0}, - {0xff, 0}, - }, - /* 247 */ - { - {0x8002, 28}, - {0x8009, 28}, - {0x8017, 28}, - {0xc028, 28}, - {0x8002, 29}, - {0x8009, 29}, - {0x8017, 29}, - {0xc028, 29}, - {0x8002, 30}, - {0x8009, 30}, - {0x8017, 30}, - {0xc028, 30}, - {0x8002, 31}, - {0x8009, 31}, - {0x8017, 31}, - {0xc028, 31}, - }, - /* 248 */ - { - {0x8003, 28}, - {0x8006, 28}, - {0x800a, 28}, - {0x800f, 28}, - {0x8018, 28}, - {0x801f, 28}, - {0x8029, 28}, - {0xc038, 28}, - {0x8003, 29}, - {0x8006, 29}, - {0x800a, 29}, - {0x800f, 29}, - {0x8018, 29}, - {0x801f, 29}, - {0x8029, 29}, - {0xc038, 29}, - }, - /* 249 */ - { - {0x8003, 30}, - {0x8006, 30}, - {0x800a, 30}, - {0x800f, 30}, - {0x8018, 30}, - {0x801f, 30}, - {0x8029, 30}, - {0xc038, 30}, - {0x8003, 31}, - {0x8006, 31}, - {0x800a, 31}, - {0x800f, 31}, - {0x8018, 31}, - {0x801f, 31}, - {0x8029, 31}, - {0xc038, 31}, - }, - /* 250 */ - { - {0x8002, 127}, - {0x8009, 127}, - {0x8017, 127}, - {0xc028, 127}, - {0x8002, 220}, - {0x8009, 220}, - {0x8017, 220}, - {0xc028, 220}, - {0x8002, 249}, - {0x8009, 249}, - {0x8017, 249}, - {0xc028, 249}, - {0xc000, 10}, - {0xc000, 13}, - {0xc000, 22}, - {0x100, 0}, - }, - /* 251 */ - { - {0x8003, 127}, - {0x8006, 127}, - {0x800a, 127}, - {0x800f, 127}, - {0x8018, 127}, - {0x801f, 127}, - {0x8029, 127}, - {0xc038, 127}, - {0x8003, 220}, - {0x8006, 220}, - {0x800a, 220}, - {0x800f, 220}, - {0x8018, 220}, - {0x801f, 220}, - {0x8029, 220}, - {0xc038, 220}, - }, - /* 252 */ - { - {0x8003, 249}, - {0x8006, 249}, - {0x800a, 249}, - {0x800f, 249}, - {0x8018, 249}, - {0x801f, 249}, - {0x8029, 249}, - {0xc038, 249}, - {0x8001, 10}, - {0xc016, 10}, - {0x8001, 13}, - {0xc016, 13}, - {0x8001, 22}, - {0xc016, 22}, - {0x100, 0}, - {0x100, 0}, - }, - /* 253 */ - { - {0x8002, 10}, - {0x8009, 10}, - {0x8017, 10}, - {0xc028, 10}, - {0x8002, 13}, - {0x8009, 13}, - {0x8017, 13}, - {0xc028, 13}, - {0x8002, 22}, - {0x8009, 22}, - {0x8017, 22}, - {0xc028, 22}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - }, - /* 254 */ - { - {0x8003, 10}, - {0x8006, 10}, - {0x800a, 10}, - {0x800f, 10}, - {0x8018, 10}, - {0x801f, 10}, - {0x8029, 10}, - {0xc038, 10}, - {0x8003, 13}, - {0x8006, 13}, - {0x800a, 13}, - {0x800f, 13}, - {0x8018, 13}, - {0x801f, 13}, - {0x8029, 13}, - {0xc038, 13}, - }, - /* 255 */ - { - {0x8003, 22}, - {0x8006, 22}, - {0x800a, 22}, - {0x800f, 22}, - {0x8018, 22}, - {0x801f, 22}, - {0x8029, 22}, - {0xc038, 22}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - }, - /* 256 */ - { - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - {0x100, 0}, - }, -}; diff --git a/deps/nghttp3/lib/nghttp3_range.c b/deps/nghttp3/lib/nghttp3_range.c deleted file mode 100644 index 0ce71480d72..00000000000 --- a/deps/nghttp3/lib/nghttp3_range.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 "nghttp3_range.h" -#include "nghttp3_macro.h" - -void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) { - r->begin = begin; - r->end = end; -} - -nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, - const nghttp3_range *b) { - nghttp3_range r = {0, 0}; - uint64_t begin = nghttp3_max(a->begin, b->begin); - uint64_t end = nghttp3_min(a->end, b->end); - if (begin < end) { - nghttp3_range_init(&r, begin, end); - } - return r; -} - -uint64_t nghttp3_range_len(const nghttp3_range *r) { return r->end - r->begin; } - -int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b) { - return a->begin == b->begin && a->end == b->end; -} - -void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right, - const nghttp3_range *a, const nghttp3_range *b) { - /* Assume that b is included in a */ - left->begin = a->begin; - left->end = b->begin; - right->begin = b->end; - right->end = a->end; -} - -int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b) { - return a->end <= b->end; -} diff --git a/deps/nghttp3/lib/nghttp3_range.h b/deps/nghttp3/lib/nghttp3_range.h deleted file mode 100644 index e65dd148354..00000000000 --- a/deps/nghttp3/lib/nghttp3_range.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 NGHTTP3_RANGE_H -#define NGHTTP3_RANGE_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -/* - * nghttp3_range represents half-closed range [begin, end). - */ -typedef struct { - uint64_t begin; - uint64_t end; -} nghttp3_range; - -/* - * nghttp3_range_init initializes |r| with the range [|begin|, |end|). - */ -void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end); - -/* - * nghttp3_range_intersect returns the intersection of |a| and |b|. - * If they do not overlap, it returns empty range. - */ -nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, - const nghttp3_range *b); - -/* - * nghttp3_range_len returns the length of |r|. - */ -uint64_t nghttp3_range_len(const nghttp3_range *r); - -/* - * nghttp3_range_eq returns nonzero if |a| equals |b|, such that - * a->begin == b->begin, and a->end == b->end hold. - */ -int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b); - -/* - * nghttp3_range_cut returns the left and right range after removing - * |b| from |a|. This function assumes that |a| completely includes - * |b|. In other words, a->begin <= b->begin and b->end <= a->end - * hold. - */ -void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right, - const nghttp3_range *a, const nghttp3_range *b); - -/* - * nghttp3_range_not_after returns nonzero if the right edge of |a| - * does not go beyond of the right edge of |b|. - */ -int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b); - -#endif /* NGHTTP3_RANGE_H */ diff --git a/deps/nghttp3/lib/nghttp3_rcbuf.c b/deps/nghttp3/lib/nghttp3_rcbuf.c deleted file mode 100644 index 1c31ecebf60..00000000000 --- a/deps/nghttp3/lib/nghttp3_rcbuf.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2016 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 "nghttp3_rcbuf.h" - -#include <assert.h> - -#include "nghttp3_mem.h" -#include "nghttp3_str.h" - -int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, - const nghttp3_mem *mem) { - uint8_t *p; - - p = nghttp3_mem_malloc(mem, sizeof(nghttp3_rcbuf) + size); - if (p == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - *rcbuf_ptr = (void *)p; - - (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; - (*rcbuf_ptr)->free = mem->free; - (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf); - (*rcbuf_ptr)->len = size; - (*rcbuf_ptr)->ref = 1; - - return 0; -} - -int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, - size_t srclen, const nghttp3_mem *mem) { - int rv; - uint8_t *p; - - rv = nghttp3_rcbuf_new(rcbuf_ptr, srclen + 1, mem); - if (rv != 0) { - return rv; - } - - (*rcbuf_ptr)->len = srclen; - p = (*rcbuf_ptr)->base; - - if (srclen) { - p = nghttp3_cpymem(p, src, srclen); - } - - *p = '\0'; - - return 0; -} - -/* - * Frees |rcbuf| itself, regardless of its reference cout. - */ -void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) { - nghttp3_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); -} - -void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) { - if (rcbuf->ref == -1) { - return; - } - - ++rcbuf->ref; -} - -void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf) { - if (rcbuf == NULL || rcbuf->ref == -1) { - return; - } - - assert(rcbuf->ref > 0); - - if (--rcbuf->ref == 0) { - nghttp3_rcbuf_del(rcbuf); - } -} - -nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf) { - nghttp3_vec res = {rcbuf->base, rcbuf->len}; - return res; -} - -int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf) { - return rcbuf->ref == -1; -} diff --git a/deps/nghttp3/lib/nghttp3_rcbuf.h b/deps/nghttp3/lib/nghttp3_rcbuf.h deleted file mode 100644 index feea8040005..00000000000 --- a/deps/nghttp3/lib/nghttp3_rcbuf.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2016 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. - */ -#ifndef NGHTTP3_RCBUF_H -#define NGHTTP3_RCBUF_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -struct nghttp3_rcbuf { - /* custom memory allocator belongs to the mem parameter when - creating this object. */ - void *mem_user_data; - nghttp3_free free; - /* The pointer to the underlying buffer */ - uint8_t *base; - /* Size of buffer pointed by |base|. */ - size_t len; - /* Reference count */ - int32_t ref; -}; - -/* - * Allocates nghttp3_rcbuf object with |size| as initial buffer size. - * When the function succeeds, the reference count becomes 1. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM: - * Out of memory. - */ -int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, - const nghttp3_mem *mem); - -/* - * Like nghttp3_rcbuf_new(), but initializes the buffer with |src| of - * length |srclen|. This function allocates additional byte at the - * end and puts '\0' into it, so that the resulting buffer could be - * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to - * |srclen|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM: - * Out of memory. - */ -int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, - size_t srclen, const nghttp3_mem *mem); - -/* - * Frees |rcbuf| itself, regardless of its reference cout. - */ -void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf); - -#endif /* NGHTTP3_RCBUF_H */ diff --git a/deps/nghttp3/lib/nghttp3_ringbuf.c b/deps/nghttp3/lib/nghttp3_ringbuf.c deleted file mode 100644 index 9ea91c81c8a..00000000000 --- a/deps/nghttp3/lib/nghttp3_ringbuf.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 "nghttp3_ringbuf.h" - -#include <assert.h> -#include <string.h> -#ifdef WIN32 -# include <intrin.h> -#endif - -#include "nghttp3_macro.h" - -#if defined(_MSC_VER) && defined(_M_ARM64) -unsigned int __popcnt(unsigned int x) { - unsigned int c = 0; - for (; x; ++c) { - x &= x - 1; - } - return c; -} -#endif - -int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, - const nghttp3_mem *mem) { - if (nmemb) { -#ifdef WIN32 - assert(1 == __popcnt((unsigned int)nmemb)); -#else - assert(1 == __builtin_popcount((unsigned int)nmemb)); -#endif - - rb->buf = nghttp3_mem_malloc(mem, nmemb * size); - if (rb->buf == NULL) { - return NGHTTP3_ERR_NOMEM; - } - } else { - rb->buf = NULL; - } - - rb->mem = mem; - rb->nmemb = nmemb; - rb->size = size; - rb->first = 0; - rb->len = 0; - - return 0; -} - -void nghttp3_ringbuf_free(nghttp3_ringbuf *rb) { - if (rb == NULL) { - return; - } - - nghttp3_mem_free(rb->mem, rb->buf); -} - -void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb) { - rb->first = (rb->first - 1) & (rb->nmemb - 1); - rb->len = nghttp3_min(rb->nmemb, rb->len + 1); - - return (void *)&rb->buf[rb->first * rb->size]; -} - -void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb) { - size_t offset = (rb->first + rb->len) & (rb->nmemb - 1); - - if (rb->len == rb->nmemb) { - rb->first = (rb->first + 1) & (rb->nmemb - 1); - } else { - ++rb->len; - } - - return (void *)&rb->buf[offset * rb->size]; -} - -void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb) { - rb->first = (rb->first + 1) & (rb->nmemb - 1); - --rb->len; -} - -void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb) { - assert(rb->len); - --rb->len; -} - -void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len) { - assert(len <= rb->nmemb); - rb->len = len; -} - -void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset) { - assert(offset < rb->len); - offset = (rb->first + offset) & (rb->nmemb - 1); - return &rb->buf[offset * rb->size]; -} - -int nghttp3_ringbuf_full(nghttp3_ringbuf *rb) { return rb->len == rb->nmemb; } - -int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb) { - uint8_t *buf; - - if (rb->nmemb >= nmemb) { - return 0; - } - -#ifdef WIN32 - assert(1 == __popcnt((unsigned int)nmemb)); -#else - assert(1 == __builtin_popcount((unsigned int)nmemb)); -#endif - - buf = nghttp3_mem_malloc(rb->mem, nmemb * rb->size); - if (buf == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - if (rb->buf != NULL) { - if (rb->first + rb->len <= rb->nmemb) { - memcpy(buf, rb->buf + rb->first * rb->size, rb->len * rb->size); - rb->first = 0; - } else { - memcpy(buf, rb->buf + rb->first * rb->size, - (rb->nmemb - rb->first) * rb->size); - memcpy(buf + (rb->nmemb - rb->first) * rb->size, rb->buf, - (rb->len - (rb->nmemb - rb->first)) * rb->size); - rb->first = 0; - } - - nghttp3_mem_free(rb->mem, rb->buf); - } - - rb->buf = buf; - rb->nmemb = nmemb; - - return 0; -} diff --git a/deps/nghttp3/lib/nghttp3_ringbuf.h b/deps/nghttp3/lib/nghttp3_ringbuf.h deleted file mode 100644 index 51194bd6356..00000000000 --- a/deps/nghttp3/lib/nghttp3_ringbuf.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 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 NGHTTP3_RINGBUF_H -#define NGHTTP3_RINGBUF_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_mem.h" - -typedef struct { - /* buf points to the underlying buffer. */ - uint8_t *buf; - const nghttp3_mem *mem; - /* nmemb is the number of elements that can be stored in this ring - buffer. */ - size_t nmemb; - /* size is the size of each element. */ - size_t size; - /* first is the offset to the first element. */ - size_t first; - /* len is the number of elements actually stored. */ - size_t len; -} nghttp3_ringbuf; - -/* - * nghttp3_ringbuf_init initializes |rb|. |nmemb| is the number of - * elements that can be stored in this buffer. |size| is the size of - * each element. |size| must be power of 2. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. - */ -int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, - const nghttp3_mem *mem); - -/* - * nghttp3_ringbuf_free frees resources allocated for |rb|. This - * function does not free the memory pointed by |rb|. - */ -void nghttp3_ringbuf_free(nghttp3_ringbuf *rb); - -/* nghttp3_ringbuf_push_front moves the offset to the first element in - the buffer backward, and returns the pointer to the element. - Caller can store data to the buffer pointed by the returned - pointer. If this action exceeds the capacity of the ring buffer, - the last element is silently overwritten, and rb->len remains - unchanged. */ -void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb); - -/* nghttp3_ringbuf_push_back moves the offset to the last element in - the buffer forward, and returns the pointer to the element. Caller - can store data to the buffer pointed by the returned pointer. If - this action exceeds the capacity of the ring buffer, the first - element is silently overwritten, and rb->len remains unchanged. */ -void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb); - -/* - * nghttp3_ringbuf_pop_front removes first element in |rb|. - */ -void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb); - -/* - * nghttp3_ringbuf_pop_back removes the last element in |rb|. - */ -void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb); - -/* nghttp3_ringbuf_resize changes the number of elements stored. This - does not change the capacity of the underlying buffer. */ -void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len); - -/* nghttp3_ringbuf_get returns the pointer to the element at - |offset|. */ -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) - -/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */ -int nghttp3_ringbuf_full(nghttp3_ringbuf *rb); - -int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb); - -#endif /* NGHTTP3_RINGBUF_H */ diff --git a/deps/nghttp3/lib/nghttp3_str.c b/deps/nghttp3/lib/nghttp3_str.c deleted file mode 100644 index 3782aa72cd6..00000000000 --- a/deps/nghttp3/lib/nghttp3_str.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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 "nghttp3_str.h" - -#include <string.h> -#include <assert.h> - -uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n) { - memcpy(dest, src, n); - return dest + n; -} - -/* Generated by gendowncasetbl.py */ -static const uint8_t DOWNCASE_TBL[] = { - 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, - 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, - 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, - 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, - 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, - 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, - 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, - 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, - 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, - 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, - 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, - 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, - 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, - 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, - 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, - 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, - 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, - 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, - 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, - 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, - 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, - 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, - 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, - 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, - 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, - 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, - 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, - 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, - 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, - 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, - 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, - 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, - 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, - 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, - 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, - 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, - 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, - 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, - 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, - 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, - 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, - 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, - 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, - 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, - 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, - 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, - 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, - 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, - 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, - 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, - 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, - 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, - 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, - 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, - 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, - 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, - 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, - 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, - 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, - 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, - 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, - 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, - 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, - 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, -}; - -void nghttp3_downcase(uint8_t *s, size_t len) { - size_t i; - for (i = 0; i < len; ++i) { - s[i] = DOWNCASE_TBL[s[i]]; - } -} diff --git a/deps/nghttp3/lib/nghttp3_str.h b/deps/nghttp3/lib/nghttp3_str.h deleted file mode 100644 index 19c1d2c71b5..00000000000 --- a/deps/nghttp3/lib/nghttp3_str.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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. - */ -#ifndef NGHTTP3_STR_H -#define NGHTTP3_STR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n); - -void nghttp3_downcase(uint8_t *s, size_t len); - -#endif /* NGHTTP3_STR_H */ diff --git a/deps/nghttp3/lib/nghttp3_stream.c b/deps/nghttp3/lib/nghttp3_stream.c deleted file mode 100644 index bf66d738d78..00000000000 --- a/deps/nghttp3/lib/nghttp3_stream.c +++ /dev/null @@ -1,1282 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_stream.h" - -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "nghttp3_conv.h" -#include "nghttp3_macro.h" -#include "nghttp3_frame.h" -#include "nghttp3_conn.h" -#include "nghttp3_str.h" -#include "nghttp3_http.h" - -/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which - makes a copy to outq. */ -#define NGHTTP3_STREAM_MAX_COPY_THRES 128 - -/* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */ -#define NGHTTP3_MIN_RBLEN 4 - -int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, - uint64_t seq, const nghttp3_stream_callbacks *callbacks, - const nghttp3_mem *mem) { - int rv; - nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream)); - nghttp3_node_id nid; - - if (stream == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - nghttp3_tnode_init( - &stream->node, - nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_STREAM, stream_id), seq, - NGHTTP3_DEFAULT_URGENCY); - - rv = nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); - if (rv != 0) { - goto frq_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); - if (rv != 0) { - goto chunks_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); - if (rv != 0) { - goto outq_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); - if (rv != 0) { - goto inq_init_fail; - } - - nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); - - stream->me.key = (key_type)stream_id; - 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 = NGHTTP3_DEFAULT_URGENCY; - stream->error_code = NGHTTP3_H3_NO_ERROR; - - if (callbacks) { - stream->callbacks = *callbacks; - } - - *pstream = stream; - - return 0; - -inq_init_fail: - nghttp3_ringbuf_free(&stream->outq); -outq_init_fail: - nghttp3_ringbuf_free(&stream->chunks); -chunks_init_fail: - nghttp3_ringbuf_free(&stream->frq); -frq_init_fail: - nghttp3_mem_free(mem, stream); - - return rv; -} - -static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) { - nghttp3_typed_buf *tbuf; - size_t i, len = nghttp3_ringbuf_len(outq); - - for (i = 0; i < len; ++i) { - tbuf = nghttp3_ringbuf_get(outq, i); - if (tbuf->type == NGHTTP3_BUF_TYPE_PRIVATE) { - nghttp3_buf_free(&tbuf->buf, mem); - } - } - - nghttp3_ringbuf_free(outq); -} - -static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) { - nghttp3_buf *buf; - size_t i, len = nghttp3_ringbuf_len(chunks); - - for (i = 0; i < len; ++i) { - buf = nghttp3_ringbuf_get(chunks, i); - nghttp3_buf_free(buf, mem); - } - - nghttp3_ringbuf_free(chunks); -} - -static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { - nghttp3_frame_entry *frent; - size_t i, len = nghttp3_ringbuf_len(frq); - - for (i = 0; i < len; ++i) { - frent = nghttp3_ringbuf_get(frq, i); - switch (frent->fr.hd.type) { - case NGHTTP3_FRAME_HEADERS: - nghttp3_frame_headers_free(&frent->fr.headers, mem); - break; - case NGHTTP3_FRAME_PUSH_PROMISE: - nghttp3_frame_push_promise_free(&frent->fr.push_promise, mem); - break; - default: - break; - } - } - - nghttp3_ringbuf_free(frq); -} - -void nghttp3_stream_del(nghttp3_stream *stream) { - if (stream == NULL) { - return; - } - - nghttp3_qpack_stream_context_free(&stream->qpack_sctx); - delete_chunks(&stream->inq, stream->mem); - delete_outq(&stream->outq, stream->mem); - delete_chunks(&stream->chunks, stream->mem); - delete_frq(&stream->frq, stream->mem); - nghttp3_tnode_free(&stream->node); - - nghttp3_mem_free(stream->mem, stream); -} - -void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) { - memset(rvint, 0, sizeof(*rvint)); -} - -void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) { - memset(rstate, 0, sizeof(*rstate)); -} - -nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, - const uint8_t *src, size_t srclen, int fin) { - size_t nread = 0; - size_t n; - size_t i; - - assert(srclen > 0); - - if (rvint->left == 0) { - assert(rvint->acc == 0); - - rvint->left = nghttp3_get_varint_len(src); - if (rvint->left <= srclen) { - rvint->acc = nghttp3_get_varint(&nread, src); - rvint->left = 0; - return (nghttp3_ssize)nread; - } - - if (fin) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - rvint->acc = nghttp3_get_varint_fb(src); - nread = 1; - ++src; - --srclen; - --rvint->left; - } - - n = nghttp3_min(rvint->left, srclen); - - for (i = 0; i < n; ++i) { - rvint->acc = (rvint->acc << 8) + src[i]; - } - - rvint->left -= n; - nread += n; - - if (fin && rvint->left) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - return (nghttp3_ssize)nread; -} - -int nghttp3_stream_frq_add(nghttp3_stream *stream, - const nghttp3_frame_entry *frent) { - nghttp3_ringbuf *frq = &stream->frq; - nghttp3_frame_entry *dest; - int rv; - - if (nghttp3_ringbuf_full(frq)) { - size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(frq) * 2); - rv = nghttp3_ringbuf_reserve(frq, nlen); - if (rv != 0) { - return rv; - } - } - - dest = nghttp3_ringbuf_push_back(frq); - *dest = *frent; - - return 0; -} - -int nghttp3_stream_fill_outq(nghttp3_stream *stream) { - nghttp3_ringbuf *frq = &stream->frq; - nghttp3_frame_entry *frent; - int data_eof; - int rv; - - for (; nghttp3_ringbuf_len(frq) && !nghttp3_stream_outq_is_full(stream) && - stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) { - frent = nghttp3_ringbuf_get(frq, 0); - - switch (frent->fr.hd.type) { - case NGHTTP3_FRAME_SETTINGS: - rv = nghttp3_stream_write_settings(stream, frent); - if (rv != 0) { - return rv; - } - break; - case NGHTTP3_FRAME_HEADERS: - rv = nghttp3_stream_write_headers(stream, frent); - if (rv != 0) { - return rv; - } - nghttp3_frame_headers_free(&frent->fr.headers, stream->mem); - break; - case NGHTTP3_FRAME_PUSH_PROMISE: - rv = nghttp3_stream_write_push_promise(stream, frent); - if (rv != 0) { - return rv; - } - nghttp3_frame_push_promise_free(&frent->fr.push_promise, stream->mem); - break; - case NGHTTP3_FRAME_CANCEL_PUSH: - rv = nghttp3_stream_write_cancel_push(stream, frent); - if (rv != 0) { - return rv; - } - break; - case NGHTTP3_FRAME_DATA: - rv = nghttp3_stream_write_data(stream, &data_eof, frent); - if (rv != 0) { - return rv; - } - if (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED) { - return 0; - } - if (!data_eof) { - return 0; - } - break; - case NGHTTP3_FRAME_MAX_PUSH_ID: - rv = nghttp3_stream_write_max_push_id(stream, frent); - if (rv != 0) { - return rv; - } - break; - default: - /* TODO Not implemented */ - break; - } - - nghttp3_ringbuf_pop_front(frq); - } - - 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_varint_len((int64_t)stream->type); - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - int rv; - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - -int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream) { - size_t len; - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - int rv; - nghttp3_push_promise *pp = stream->pp; - - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - assert(pp); - - len = nghttp3_put_varint_len((int64_t)stream->type) + - nghttp3_put_varint_len(pp->node.nid.id); - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); - chunk->last = nghttp3_put_varint(chunk->last, pp->node.nid.id); - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - -int nghttp3_stream_write_settings(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - size_t len; - int rv; - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - struct { - nghttp3_frame_settings settings; - nghttp3_settings_entry iv[15]; - } fr; - nghttp3_settings_entry *iv; - nghttp3_conn_settings *local_settings = frent->aux.settings.local_settings; - - 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; - iv[0].value = local_settings->max_field_section_size; - iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY; - iv[1].value = local_settings->qpack_max_table_capacity; - iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS; - iv[2].value = local_settings->qpack_blocked_streams; - - len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings); - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings); - - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - -int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_cancel_push *fr = &frent->fr.cancel_push; - size_t len; - int rv; - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - - len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr); - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_frame_write_cancel_push(chunk->last, fr); - - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - -int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_max_push_id *fr = &frent->fr.max_push_id; - nghttp3_conn *conn = stream->conn; - size_t len; - int rv; - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - - assert(conn); - assert(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED); - - fr->push_id = (int64_t)conn->remote.uni.unsent_max_pushes - 1; - conn->remote.uni.max_pushes = conn->remote.uni.unsent_max_pushes; - conn->flags &= (uint16_t)~NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; - - len = nghttp3_frame_write_max_push_id_len(&fr->hd.length, fr); - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_frame_write_max_push_id(chunk->last, fr); - - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - -int nghttp3_stream_write_headers(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_headers *fr = &frent->fr.headers; - nghttp3_conn *conn = stream->conn; - - assert(conn); - - return nghttp3_stream_write_header_block( - stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, - &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, 0, fr->nva, fr->nvlen); -} - -int nghttp3_stream_write_push_promise(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_push_promise *fr = &frent->fr.push_promise; - nghttp3_conn *conn = stream->conn; - - assert(conn); - - return nghttp3_stream_write_header_block( - stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, - &conn->tx.qpack.ebuf, NGHTTP3_FRAME_PUSH_PROMISE, fr->push_id, fr->nva, - fr->nvlen); -} - -int nghttp3_stream_write_header_block(nghttp3_stream *stream, - nghttp3_qpack_encoder *qenc, - nghttp3_stream *qenc_stream, - nghttp3_buf *rbuf, nghttp3_buf *ebuf, - int64_t frame_type, int64_t push_id, - const nghttp3_nv *nva, size_t nvlen) { - nghttp3_buf pbuf; - int rv; - size_t len; - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - nghttp3_frame_hd hd; - size_t push_idlen = 0; - uint8_t raw_pbuf[16]; - size_t pbuflen, rbuflen, ebuflen; - - nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf)); - - rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, - stream->node.nid.id, nva, nvlen); - if (rv != 0) { - goto fail; - } - - pbuflen = nghttp3_buf_len(&pbuf); - rbuflen = nghttp3_buf_len(rbuf); - ebuflen = nghttp3_buf_len(ebuf); - - if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { - push_idlen = nghttp3_put_varint_len(push_id); - } - - hd.type = frame_type; - hd.length = (int64_t)(pbuflen + rbuflen + push_idlen); - - len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen; - - if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) { - len += rbuflen; - } - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - goto fail; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); - - if (push_idlen) { - chunk->last = nghttp3_put_varint(chunk->last, push_id); - } - - chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen); - nghttp3_buf_init(&pbuf); - - if (rbuflen > NGHTTP3_STREAM_MAX_COPY_THRES) { - tbuf.buf.last = chunk->last; - - rv = nghttp3_stream_outq_add(stream, &tbuf); - if (rv != 0) { - goto fail; - } - - nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE); - rv = nghttp3_stream_outq_add(stream, &tbuf); - if (rv != 0) { - goto fail; - } - nghttp3_buf_init(rbuf); - } else if (rbuflen) { - chunk->last = nghttp3_cpymem(chunk->last, rbuf->pos, rbuflen); - tbuf.buf.last = chunk->last; - - rv = nghttp3_stream_outq_add(stream, &tbuf); - if (rv != 0) { - goto fail; - } - nghttp3_buf_reset(rbuf); - } - - if (ebuflen > NGHTTP3_STREAM_MAX_COPY_THRES) { - assert(qenc_stream); - - nghttp3_typed_buf_init(&tbuf, ebuf, NGHTTP3_BUF_TYPE_PRIVATE); - rv = nghttp3_stream_outq_add(qenc_stream, &tbuf); - if (rv != 0) { - return rv; - } - nghttp3_buf_init(ebuf); - } else if (ebuflen) { - assert(qenc_stream); - - rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen); - if (rv != 0) { - goto fail; - } - - chunk = nghttp3_stream_get_chunk(qenc_stream); - 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; - } - nghttp3_buf_reset(ebuf); - } - - assert(0 == nghttp3_buf_len(&pbuf)); - assert(0 == nghttp3_buf_len(rbuf)); - assert(0 == nghttp3_buf_len(ebuf)); - - return 0; - -fail: - - return rv; -} - -int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, - nghttp3_frame_entry *frent) { - int rv; - size_t len; - nghttp3_typed_buf tbuf; - nghttp3_buf buf; - nghttp3_buf *chunk; - nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data; - nghttp3_conn *conn = stream->conn; - size_t datalen; - uint32_t flags = 0; - nghttp3_frame_hd hd; - nghttp3_vec vec[8]; - nghttp3_vec *v; - nghttp3_ssize sveccnt; - size_t i; - - assert(!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)); - assert(read_data); - assert(conn); - - *peof = 0; - - sveccnt = read_data(conn, stream->node.nid.id, vec, nghttp3_arraylen(vec), - &flags, conn->user_data, stream->user_data); - if (sveccnt < 0) { - if (sveccnt == NGHTTP3_ERR_WOULDBLOCK) { - stream->flags |= NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; - return 0; - } - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - datalen = nghttp3_vec_len(vec, (size_t)sveccnt); - - assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF); - - if (flags & NGHTTP3_DATA_FLAG_EOF) { - *peof = 1; - if (!(flags & NGHTTP3_DATA_FLAG_NO_END_STREAM)) { - stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; - if (datalen == 0) { - if (nghttp3_stream_outq_write_done(stream)) { - /* If this is the last data and its is 0 length, we don't - need send DATA frame. We rely on the non-emptiness of - outq to schedule stream, so add empty tbuf to outq to - just send fin. */ - nghttp3_buf_init(&buf); - nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_PRIVATE); - return nghttp3_stream_outq_add(stream, &tbuf); - } - return 0; - } - } - - if (datalen == 0) { - /* We are going to send more frames, but no DATA frame this - time. */ - return 0; - } - } - - hd.type = NGHTTP3_FRAME_DATA; - hd.length = (int64_t)datalen; - - len = nghttp3_frame_write_hd_len(&hd); - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); - - tbuf.buf.last = chunk->last; - - rv = nghttp3_stream_outq_add(stream, &tbuf); - if (rv != 0) { - return rv; - } - - if (datalen) { - for (i = 0; i < (size_t)sveccnt; ++i) { - v = &vec[i]; - if (v->len == 0) { - continue; - } - nghttp3_buf_wrap_init(&buf, v->base, v->len); - buf.last = buf.end; - nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN); - rv = nghttp3_stream_outq_add(stream, &tbuf); - if (rv != 0) { - return rv; - } - } - } - - return 0; -} - -int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) { - nghttp3_qpack_decoder *qdec; - nghttp3_buf *chunk; - int rv; - nghttp3_typed_buf tbuf; - size_t len; - - assert(stream->conn); - assert(stream->conn->tx.qdec == stream); - - qdec = &stream->conn->qdec; - - assert(qdec); - - len = nghttp3_qpack_decoder_get_decoder_streamlen(qdec); - if (len == 0) { - return 0; - } - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - nghttp3_qpack_decoder_write_decoder(qdec, chunk); - - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - -int nghttp3_stream_outq_is_full(nghttp3_stream *stream) { - /* TODO Verify that the limit is reasonable. */ - return nghttp3_ringbuf_len(&stream->outq) >= 1024; -} - -int nghttp3_stream_outq_add(nghttp3_stream *stream, - const nghttp3_typed_buf *tbuf) { - nghttp3_ringbuf *outq = &stream->outq; - int rv; - nghttp3_typed_buf *dest; - size_t len = nghttp3_ringbuf_len(outq); - - stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf); - - 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) { - /* 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; - - return 0; - } - } - - if (nghttp3_ringbuf_full(outq)) { - size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2); - rv = nghttp3_ringbuf_reserve(outq, nlen); - if (rv != 0) { - return rv; - } - } - - dest = nghttp3_ringbuf_push_back(outq); - *dest = *tbuf; - - return 0; -} - -int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) { - nghttp3_ringbuf *chunks = &stream->chunks; - nghttp3_buf *chunk; - size_t len = nghttp3_ringbuf_len(chunks); - uint8_t *p; - int rv; - size_t n = NGHTTP3_STREAM_MIN_CHUNK_SIZE; - - if (len) { - chunk = nghttp3_ringbuf_get(chunks, len - 1); - if (nghttp3_buf_left(chunk) >= need) { - return 0; - } - } - - for (; n < need; n *= 2) - ; - - p = nghttp3_mem_malloc(stream->mem, n); - if (p == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - if (nghttp3_ringbuf_full(chunks)) { - size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2); - rv = nghttp3_ringbuf_reserve(chunks, nlen); - if (rv != 0) { - return rv; - } - } - - chunk = nghttp3_ringbuf_push_back(chunks); - nghttp3_buf_wrap_init(chunk, p, n); - - return 0; -} - -nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream) { - nghttp3_ringbuf *chunks = &stream->chunks; - size_t len = nghttp3_ringbuf_len(chunks); - - assert(len); - - return nghttp3_ringbuf_get(chunks, len - 1); -} - -int nghttp3_stream_is_blocked(nghttp3_stream *stream) { - return (stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) || - (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED); -} - -int nghttp3_stream_require_schedule(nghttp3_stream *stream) { - return (!nghttp3_stream_outq_write_done(stream) && - !(stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED)) || - (nghttp3_ringbuf_len(&stream->frq) && - !(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)); -} - -nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, - nghttp3_vec *vec, size_t veccnt) { - nghttp3_ringbuf *outq = &stream->outq; - size_t len = nghttp3_ringbuf_len(outq); - size_t i; - size_t offset = stream->outq_offset; - size_t buflen; - nghttp3_vec *vbegin = vec, *vend = vec + veccnt; - nghttp3_typed_buf *tbuf; - - assert(veccnt > 0); - - 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; - } - - vec->base = tbuf->buf.pos + offset; - vec->len = buflen - offset; - ++vec; - ++i; - break; - } - - 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); - } - - /* TODO Rework this if we have finished implementing HTTP - messaging */ - *pfin = nghttp3_ringbuf_len(&stream->frq) == 0 && i == len && - (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM); - - return vec - vbegin; -} - -int 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); - size_t offset = stream->outq_offset + n; - size_t buflen; - nghttp3_typed_buf *tbuf; - - 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; - } - - break; - } - - assert(i < len || offset == 0); - - stream->unsent_bytes -= n; - stream->outq_idx = i; - stream->outq_offset = offset; - - return 0; -} - -int nghttp3_stream_outq_write_done(nghttp3_stream *stream) { - nghttp3_ringbuf *outq = &stream->outq; - size_t len = nghttp3_ringbuf_len(outq); - - return len == 0 || stream->outq_idx >= len; -} - -static int stream_pop_outq_entry(nghttp3_stream *stream, - nghttp3_typed_buf *tbuf) { - nghttp3_ringbuf *chunks = &stream->chunks; - nghttp3_buf *chunk; - - switch (tbuf->type) { - case NGHTTP3_BUF_TYPE_PRIVATE: - nghttp3_buf_free(&tbuf->buf, stream->mem); - break; - case NGHTTP3_BUF_TYPE_ALIEN: - break; - default: - 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) { - nghttp3_buf_free(chunk, stream->mem); - nghttp3_ringbuf_pop_front(chunks); - } - }; - - nghttp3_ringbuf_pop_front(&stream->outq); - - return 0; -} - -int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) { - nghttp3_ringbuf *outq = &stream->outq; - uint64_t offset = stream->ack_offset + n; - size_t buflen; - size_t npopped = 0; - size_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); - - if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) { - nack = (size_t)nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done; - if (stream->callbacks.acked_data) { - rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack, - stream->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - stream->ack_done += nack; - } - - if (offset >= buflen) { - rv = stream_pop_outq_entry(stream, tbuf); - if (rv != 0) { - return rv; - } - - offset -= buflen; - ++npopped; - stream->ack_done = 0; - - if (stream->outq_idx + 1 == npopped) { - stream->outq_offset = 0; - break; - } - - continue; - } - - 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; -} - -int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *data, - size_t datalen) { - nghttp3_ringbuf *inq = &stream->inq; - size_t len = nghttp3_ringbuf_len(inq); - nghttp3_buf *buf; - size_t nwrite; - uint8_t *rawbuf; - size_t bufleft; - int rv; - - if (len) { - buf = nghttp3_ringbuf_get(inq, len - 1); - bufleft = nghttp3_buf_left(buf); - nwrite = nghttp3_min(datalen, bufleft); - buf->last = nghttp3_cpymem(buf->last, data, nwrite); - data += nwrite; - datalen -= nwrite; - } - - for (; datalen;) { - if (nghttp3_ringbuf_full(inq)) { - size_t nlen = - nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(inq) * 2); - rv = nghttp3_ringbuf_reserve(inq, nlen); - if (rv != 0) { - return rv; - } - } - - rawbuf = nghttp3_mem_malloc(stream->mem, 16384); - if (rawbuf == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - buf = nghttp3_ringbuf_push_back(inq); - nghttp3_buf_wrap_init(buf, rawbuf, 16384); - bufleft = nghttp3_buf_left(buf); - nwrite = nghttp3_min(datalen, bufleft); - buf->last = nghttp3_cpymem(buf->last, data, nwrite); - data += nwrite; - datalen -= nwrite; - } - - return 0; -} - -size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream) { - nghttp3_ringbuf *inq = &stream->inq; - size_t len = nghttp3_ringbuf_len(inq); - size_t i, n = 0; - nghttp3_buf *buf; - - for (i = 0; i < len; ++i) { - buf = nghttp3_ringbuf_get(inq, i); - n += nghttp3_buf_len(buf); - } - - return n; -} - -int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, - nghttp3_stream_http_event event) { - int rv; - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_NONE: - return NGHTTP3_ERR_H3_INTERNAL_ERROR; - 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: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END; - return 0; - case NGHTTP3_HTTP_STATE_REQ_HEADERS_END: - switch (event) { - case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: - /* TODO Better to check status code */ - if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_DATA_BEGIN: - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; - return 0; - default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_DATA_END) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END; - return 0; - case NGHTTP3_HTTP_STATE_REQ_DATA_END: - switch (event) { - case NGHTTP3_HTTP_EVENT_DATA_BEGIN: - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: - /* TODO Better to check status code */ - if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; - return 0; - default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END; - return 0; - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END: - if (event != NGHTTP3_HTTP_EVENT_MSG_END) { - /* TODO Should ignore unexpected frame in this state as per - spec. */ - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; - return 0; - case NGHTTP3_HTTP_STATE_REQ_END: - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - case NGHTTP3_HTTP_STATE_RESP_INITIAL: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - 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_H3_GENERAL_PROTOCOL_ERROR; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END; - return 0; - case NGHTTP3_HTTP_STATE_RESP_HEADERS_END: - switch (event) { - case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: - if (stream->rx.http.status_code == -1) { - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN; - return 0; - } - if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) && - stream->rx.http.status_code / 100 == 2) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_DATA_BEGIN: - if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; - return 0; - default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_DATA_END) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END; - return 0; - case NGHTTP3_HTTP_STATE_RESP_DATA_END: - switch (event) { - case NGHTTP3_HTTP_EVENT_DATA_BEGIN: - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: - if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) && - stream->rx.http.status_code / 100 == 2) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; - return 0; - default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - 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_H3_GENERAL_PROTOCOL_ERROR; - } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; - return 0; - case NGHTTP3_HTTP_STATE_RESP_END: - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - default: - assert(0); - } -} - -int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) { - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - return 0; - default: - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } -} - -int nghttp3_stream_bidi_or_push(nghttp3_stream *stream) { - return (!nghttp3_stream_uni(stream->node.nid.id) || - stream->type == NGHTTP3_STREAM_TYPE_PUSH); -} - -int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; } - -int nghttp3_client_stream_bidi(int64_t stream_id) { - return (stream_id & 0x3) == 0; -} - -int nghttp3_client_stream_uni(int64_t stream_id) { - return (stream_id & 0x3) == 0x2; -} - -int nghttp3_server_stream_uni(int64_t stream_id) { - return (stream_id & 0x3) == 0x3; -} diff --git a/deps/nghttp3/lib/nghttp3_stream.h b/deps/nghttp3/lib/nghttp3_stream.h deleted file mode 100644 index 1dcc421123a..00000000000 --- a/deps/nghttp3/lib/nghttp3_stream.h +++ /dev/null @@ -1,406 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_STREAM_H -#define NGHTTP3_STREAM_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_map.h" -#include "nghttp3_tnode.h" -#include "nghttp3_ringbuf.h" -#include "nghttp3_buf.h" -#include "nghttp3_frame.h" -#include "nghttp3_qpack.h" - -#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256 - -/* NGHTTP3_MIN_UNSENT_BYTES is the minimum unsent bytes which is large - enough to fill outgoing single QUIC packet. */ -#define NGHTTP3_MIN_UNSENT_BYTES 4096 - -/* NGHTTP3_STREAM_MIN_WRITELEN is the minimum length of write to cause - the stream to reschedule. */ -#define NGHTTP3_STREAM_MIN_WRITELEN 800 - -/* nghttp3_stream_type is unidirectional stream type. */ -typedef enum { - NGHTTP3_STREAM_TYPE_CONTROL = 0x00, - NGHTTP3_STREAM_TYPE_PUSH = 0x01, - NGHTTP3_STREAM_TYPE_QPACK_ENCODER = 0x02, - NGHTTP3_STREAM_TYPE_QPACK_DECODER = 0x03, - NGHTTP3_STREAM_TYPE_UNKNOWN = UINT64_MAX, -} nghttp3_stream_type; - -typedef enum { - NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE, - NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH, - NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH, - NGHTTP3_CTRL_STREAM_STATE_SETTINGS, - NGHTTP3_CTRL_STREAM_STATE_GOAWAY, - NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID, - NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME, - NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID, - NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE, -} nghttp3_ctrl_stream_state; - -typedef enum { - NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE, - NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH, - NGHTTP3_REQ_STREAM_STATE_DATA, - NGHTTP3_REQ_STREAM_STATE_HEADERS, - NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID, - NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE, - NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE, - NGHTTP3_REQ_STREAM_STATE_IGN_FRAME, -} nghttp3_req_stream_state; - -typedef enum { - NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE, - NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH, - NGHTTP3_PUSH_STREAM_STATE_DATA, - NGHTTP3_PUSH_STREAM_STATE_HEADERS, - NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME, - NGHTTP3_PUSH_STREAM_STATE_PUSH_ID, - NGHTTP3_PUSH_STREAM_STATE_IGN_REST, -} nghttp3_push_stream_state; - -typedef struct { - int64_t acc; - size_t left; -} nghttp3_varint_read_state; - -typedef struct { - nghttp3_varint_read_state rvint; - nghttp3_frame fr; - int state; - int64_t left; -} nghttp3_stream_read_state; - -typedef enum { - NGHTTP3_STREAM_FLAG_NONE = 0x0000, - NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED = 0x0001, - /* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is - blocked by QUIC flow control. */ - NGHTTP3_STREAM_FLAG_FC_BLOCKED = 0x0002, - /* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application - is temporarily unable to provide data. */ - NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED = 0x0004, - /* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application - finished to feed outgoing data. */ - NGHTTP3_STREAM_FLAG_WRITE_END_STREAM = 0x0008, - /* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is - blocked due to QPACK decoding. */ - NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED = 0x0010, - /* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent - fin. */ - NGHTTP3_STREAM_FLAG_READ_EOF = 0x0020, - /* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed. - nghttp3_stream object can still alive because it might be blocked - by QPACK decoder. */ - NGHTTP3_STREAM_FLAG_CLOSED = 0x0040, - /* NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED indicates that stream is - blocked because the corresponding PUSH_PROMISE has not been - received yet. */ - NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED = 0x0080, - /* NGHTTP3_STREAM_FLAG_RESET indicates that stream is reset. */ - NGHTTP3_STREAM_FLAG_RESET = 0x0200, -} nghttp3_stream_flag; - -typedef enum { - NGHTTP3_HTTP_STATE_NONE, - NGHTTP3_HTTP_STATE_REQ_INITIAL, - NGHTTP3_HTTP_STATE_REQ_BEGIN, - NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN, - NGHTTP3_HTTP_STATE_REQ_HEADERS_END, - NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN, - NGHTTP3_HTTP_STATE_REQ_DATA_END, - NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN, - NGHTTP3_HTTP_STATE_REQ_TRAILERS_END, - NGHTTP3_HTTP_STATE_REQ_END, - NGHTTP3_HTTP_STATE_RESP_INITIAL, - NGHTTP3_HTTP_STATE_RESP_BEGIN, - NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN, - NGHTTP3_HTTP_STATE_RESP_HEADERS_END, - NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN, - NGHTTP3_HTTP_STATE_RESP_DATA_END, - NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN, - NGHTTP3_HTTP_STATE_RESP_TRAILERS_END, - NGHTTP3_HTTP_STATE_RESP_END, -} nghttp3_stream_http_state; - -typedef enum { - NGHTTP3_HTTP_EVENT_DATA_BEGIN, - NGHTTP3_HTTP_EVENT_DATA_END, - NGHTTP3_HTTP_EVENT_HEADERS_BEGIN, - NGHTTP3_HTTP_EVENT_HEADERS_END, - NGHTTP3_HTTP_EVENT_PUSH_PROMISE_BEGIN, - NGHTTP3_HTTP_EVENT_PUSH_PROMISE_END, - NGHTTP3_HTTP_EVENT_MSG_END, -} nghttp3_stream_http_event; - -struct nghttp3_stream; -typedef struct nghttp3_stream nghttp3_stream; - -struct nghttp3_push_promise; -typedef struct nghttp3_push_promise nghttp3_push_promise; - -/* - * nghttp3_stream_acked_data is a callback function which is invoked - * when data sent on stream denoted by |stream_id| supplied from - * application is acknowledged by remote endpoint. The number of - * bytes acknowledged is given in |datalen|. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning NGHTTP3_ERR_CALLBACK_FAILURE will return to the caller - * immediately. Any values other than 0 is treated as - * NGHTTP3_ERR_CALLBACK_FAILURE. - */ -typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream, - int64_t stream_id, size_t datalen, - void *user_data); - -typedef struct { - nghttp3_stream_acked_data acked_data; -} nghttp3_stream_callbacks; - -struct nghttp3_http_state { - /* status_code is HTTP status code received. This field is used - if connection is initialized as client. */ - int32_t status_code; - /* content_length is the value of received content-length header - field. */ - int64_t content_length; - /* recv_content_length is the number of body bytes received so - far. */ - int64_t recv_content_length; - uint16_t flags; - /* pri is a stream priority produced by nghttp3_pri_to_uint8. */ - uint8_t pri; -}; - -typedef struct nghttp3_http_state nghttp3_http_state; - -struct nghttp3_stream { - const nghttp3_mem *mem; - nghttp3_map_entry me; - /* node is a node in dependency tree. For server initiated - unidirectional stream (push), scheduling is done via - corresponding nghttp3_push_promise object pointed by pp. */ - nghttp3_tnode node; - nghttp3_pq_entry qpack_blocked_pe; - nghttp3_stream_callbacks callbacks; - nghttp3_ringbuf frq; - nghttp3_ringbuf chunks; - nghttp3_ringbuf outq; - /* inq stores the stream raw data which cannot be read because - stream is blocked by QPACK decoder. */ - nghttp3_ringbuf inq; - nghttp3_qpack_stream_context qpack_sctx; - /* conn is a reference to underlying connection. It could be NULL - if stream is not a request/push stream. */ - nghttp3_conn *conn; - void *user_data; - /* unsent_bytes is the number of bytes in outq not written yet */ - size_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. */ - size_t outq_offset; - /* ack_offset is offset acknowledged by peer relative to the first - element in outq. */ - uint64_t ack_offset; - /* ack_done is the number of bytes notified to an application that - they are acknowledged inside the first outq element if it is of - type NGHTTP3_BUF_TYPE_ALIEN. */ - size_t ack_done; - size_t unscheduled_nwrite; - nghttp3_stream_type type; - nghttp3_stream_read_state rstate; - /* pp is nghttp3_push_promise that this stream fulfills. */ - nghttp3_push_promise *pp; - /* error_code indicates the reason of closure of this stream. */ - uint64_t error_code; - - struct { - nghttp3_stream_http_state hstate; - } tx; - - struct { - nghttp3_stream_http_state hstate; - nghttp3_http_state http; - } rx; - - uint16_t flags; -}; - -typedef struct { - nghttp3_frame fr; - union { - struct { - nghttp3_conn_settings *local_settings; - } settings; - struct { - nghttp3_data_reader dr; - } data; - } aux; -} nghttp3_frame_entry; - -int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, - uint64_t seq, const nghttp3_stream_callbacks *callbacks, - const nghttp3_mem *mem); - -void nghttp3_stream_del(nghttp3_stream *stream); - -void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint); - -void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate); - -nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, - const uint8_t *src, size_t srclen, int fin); - -int nghttp3_stream_frq_add(nghttp3_stream *stream, - const nghttp3_frame_entry *frent); - -int nghttp3_stream_fill_outq(nghttp3_stream *stream); - -int nghttp3_stream_write_stream_type(nghttp3_stream *stream); - -int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream); - -nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, - nghttp3_vec *vec, size_t veccnt); - -int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream); - -int nghttp3_stream_outq_is_full(nghttp3_stream *stream); - -int nghttp3_stream_outq_add(nghttp3_stream *stream, - const nghttp3_typed_buf *tbuf); - -int nghttp3_stream_write_headers(nghttp3_stream *stream, - nghttp3_frame_entry *frent); - -int nghttp3_stream_write_push_promise(nghttp3_stream *stream, - nghttp3_frame_entry *frent); - -int nghttp3_stream_write_header_block(nghttp3_stream *stream, - nghttp3_qpack_encoder *qenc, - nghttp3_stream *qenc_stream, - nghttp3_buf *rbuf, nghttp3_buf *ebuf, - int64_t frame_type, int64_t push_id, - const nghttp3_nv *nva, size_t nvlen); - -int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, - nghttp3_frame_entry *frent); - -int nghttp3_stream_write_settings(nghttp3_stream *stream, - nghttp3_frame_entry *frent); - -int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, - nghttp3_frame_entry *frent); - -int nghttp3_stream_write_max_push_id(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); - -int nghttp3_stream_is_blocked(nghttp3_stream *stream); - -int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n); - -/* - * nghttp3_stream_outq_write_done returns nonzero if all contents in - * outq have been written. - */ -int nghttp3_stream_outq_write_done(nghttp3_stream *stream); - -int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n); - -/* - * nghttp3_stream_is_active returns nonzero if |stream| is active. In - * other words, it has something to send. This function does not take - * into account its descendants. - */ -int nghttp3_stream_is_active(nghttp3_stream *stream); - -/* - * nghttp3_stream_require_schedule returns nonzero if |stream| should - * be scheduled. In other words, |stream| or its descendants have - * something to send. - */ -int nghttp3_stream_require_schedule(nghttp3_stream *stream); - -int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *src, - size_t srclen); - -size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream); - -int nghttp3_stream_ensure_qpack_stream_context(nghttp3_stream *stream); - -void nghttp3_stream_delete_qpack_stream_context(nghttp3_stream *stream); - -int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, - nghttp3_stream_http_event event); - -int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream); - -/* - * nghttp3_stream_bidi_or_push returns nonzero if |stream| is - * bidirectional or push stream. - */ -int nghttp3_stream_bidi_or_push(nghttp3_stream *stream); - -/* - * nghttp3_stream_uni returns nonzero if stream identified by - * |stream_id| is unidirectional. - */ -int nghttp3_stream_uni(int64_t stream_id); - -/* - * nghttp3_client_stream_bidi returns nonzero if stream identified by - * |stream_id| is client initiated bidirectional stream. - */ -int nghttp3_client_stream_bidi(int64_t stream_id); - -/* - * nghttp3_client_stream_uni returns nonzero if stream identified by - * |stream_id| is client initiated unidirectional stream. - */ -int nghttp3_client_stream_uni(int64_t stream_id); - -/* - * nghttp3_server_stream_uni returns nonzero if stream identified by - * |stream_id| is server initiated unidirectional stream. - */ -int nghttp3_server_stream_uni(int64_t stream_id); - -#endif /* NGHTTP3_STREAM_H */ diff --git a/deps/nghttp3/lib/nghttp3_tnode.c b/deps/nghttp3/lib/nghttp3_tnode.c deleted file mode 100644 index 94dca7dbf76..00000000000 --- a/deps/nghttp3/lib/nghttp3_tnode.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_tnode.h" - -#include <assert.h> - -#include "nghttp3_macro.h" -#include "nghttp3_stream.h" -#include "nghttp3_conn.h" -#include "nghttp3_conv.h" - -nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid, - nghttp3_node_id_type type, int64_t id) { - nid->type = type; - nid->id = id; - return nid; -} - -int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b) { - return a->type == b->type && a->id == b->id; -} - -void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, - uint64_t seq, uint8_t pri) { - assert(nghttp3_pri_uint8_urgency(pri) < NGHTTP3_URGENCY_LEVELS); - - tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; - tnode->nid = *nid; - tnode->seq = seq; - tnode->cycle = 0; - tnode->pri = pri; -} - -void nghttp3_tnode_free(nghttp3_tnode *tnode) { (void)tnode; } - -static void tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) { - assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX); - - nghttp3_pq_remove(pq, &tnode->pe); - tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; -} - -void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) { - if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { - return; - } - - tnode_unschedule(tnode, pq); -} - -static uint64_t pq_get_first_cycle(nghttp3_pq *pq) { - nghttp3_tnode *top; - - if (nghttp3_pq_empty(pq)) { - return 0; - } - - top = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); - return top->cycle; -} - -int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, - size_t nwrite) { - uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN; - - if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { - tnode->cycle = pq_get_first_cycle(pq) + - ((nwrite == 0 || !nghttp3_pri_uint8_inc(tnode->pri)) - ? 0 - : nghttp3_max(1, penalty)); - } else if (nwrite > 0) { - if (!nghttp3_pri_uint8_inc(tnode->pri) || nghttp3_pq_size(pq) == 1) { - return 0; - } - - nghttp3_pq_remove(pq, &tnode->pe); - tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; - tnode->cycle += nghttp3_max(1, penalty); - } else { - return 0; - } - - return nghttp3_pq_push(pq, &tnode->pe); -} - -int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) { - return tnode->pe.index != NGHTTP3_PQ_BAD_INDEX; -} diff --git a/deps/nghttp3/lib/nghttp3_tnode.h b/deps/nghttp3/lib/nghttp3_tnode.h deleted file mode 100644 index bf861ed0409..00000000000 --- a/deps/nghttp3/lib/nghttp3_tnode.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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_TNODE_H -#define NGHTTP3_TNODE_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#include "nghttp3_pq.h" - -#define NGHTTP3_TNODE_MAX_CYCLE_GAP (1llu << 24) - -typedef enum { - NGHTTP3_NODE_ID_TYPE_STREAM = 0x00, - NGHTTP3_NODE_ID_TYPE_PUSH = 0x01, -} nghttp3_node_id_type; - -typedef struct { - nghttp3_node_id_type type; - int64_t id; -} nghttp3_node_id; - -nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid, - nghttp3_node_id_type type, int64_t id); - -int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b); - -struct nghttp3_tnode; -typedef struct nghttp3_tnode nghttp3_tnode; - -struct nghttp3_tnode { - nghttp3_pq_entry pe; - size_t num_children; - nghttp3_node_id nid; - uint64_t seq; - uint64_t cycle; - /* pri is a stream priority produced by nghttp3_pri_to_uint8. */ - uint8_t pri; -}; - -void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, - uint64_t seq, uint8_t pri); - -void nghttp3_tnode_free(nghttp3_tnode *tnode); - -void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq); - -/* - * nghttp3_tnode_schedule schedules |tnode| using |nwrite| as penalty. - * If |tnode| has already been scheduled, it is rescheduled by the - * amount of |nwrite|. - */ -int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, size_t nwrite); - -/* - * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled. - */ -int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode); - -#endif /* NGHTTP3_TNODE_H */ diff --git a/deps/nghttp3/lib/nghttp3_vec.c b/deps/nghttp3/lib/nghttp3_vec.c deleted file mode 100644 index 8d530a060dd..00000000000 --- a/deps/nghttp3/lib/nghttp3_vec.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2018 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 "nghttp3_vec.h" -#include "nghttp3_macro.h" - -size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { - size_t i; - size_t res = 0; - - for (i = 0; i < n; ++i) { - res += vec[i].len; - } - - return res; -} - -int nghttp3_vec_empty(const nghttp3_vec *vec, size_t cnt) { - size_t i; - - for (i = 0; i < cnt && vec[i].len == 0; ++i) - ; - - return i == cnt; -} - -void nghttp3_vec_consume(nghttp3_vec **pvec, size_t *pcnt, size_t len) { - nghttp3_vec *v = *pvec; - size_t cnt = *pcnt; - - for (; cnt > 0; --cnt, ++v) { - if (v->len > len) { - v->len -= len; - v->base += len; - break; - } - len -= v->len; - } - - *pvec = v; - *pcnt = cnt; -} diff --git a/deps/nghttp3/lib/nghttp3_vec.h b/deps/nghttp3/lib/nghttp3_vec.h deleted file mode 100644 index c1a928e3e1d..00000000000 --- a/deps/nghttp3/lib/nghttp3_vec.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2018 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 NGHTTP3_VEC_H -#define NGHTTP3_VEC_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -#endif /* NGHTTP3_VEC_H */ diff --git a/deps/nghttp3/lib/nghttp3_version.c b/deps/nghttp3/lib/nghttp3_version.c deleted file mode 100644 index dfad4793c4b..00000000000 --- a/deps/nghttp3/lib/nghttp3_version.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * nghttp3 - * - * Copyright (c) 2019 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. - */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp3/nghttp3.h> - -static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, - NGHTTP3_VERSION}; - -nghttp3_info *nghttp3_version(int least_version) { - if (least_version > NGHTTP3_VERSION_NUM) { - return NULL; - } - return &version; -} diff --git a/deps/nghttp3/nghttp3.gyp b/deps/nghttp3/nghttp3.gyp deleted file mode 100644 index 85ca26cbc8d..00000000000 --- a/deps/nghttp3/nghttp3.gyp +++ /dev/null @@ -1,67 +0,0 @@ -{ - 'target_defaults': { - 'defines': [ - '_U_=' - ] - }, - 'targets': [ - { - 'target_name': 'nghttp3', - 'type': 'static_library', - 'include_dirs': ['lib/includes'], - 'defines': [ - 'BUILDING_NGHTTP3', - 'NGHTTP3_STATICLIB', - ], - 'conditions': [ - ['OS=="win"', { - 'defines': [ - 'WIN32', - '_WINDOWS', - 'HAVE_CONFIG_H', - ], - 'msvs_settings': { - 'VCCLCompilerTool': { - 'CompileAs': '1' - }, - }, - }], - ['OS=="linux"', { - 'defines': [ - 'HAVE_ARPA_INET_H', - ], - }], - ], - 'direct_dependent_settings': { - 'defines': [ 'NGHTTP3_STATICLIB' ], - 'include_dirs': [ 'lib/includes' ] - }, - 'sources': [ - 'lib/nghttp3_buf.c', - 'lib/nghttp3_conv.c', - 'lib/nghttp3_err.c', - 'lib/nghttp3_gaptr.c', - 'lib/nghttp3_idtr.c', - 'lib/nghttp3_map.c', - 'lib/nghttp3_pq.c', - 'lib/nghttp3_qpack_huffman.c', - 'lib/nghttp3_range.c', - 'lib/nghttp3_ringbuf.c', - 'lib/nghttp3_stream.c', - 'lib/nghttp3_vec.c', - 'lib/nghttp3_conn.c', - 'lib/nghttp3_debug.c', - 'lib/nghttp3_frame.c', - 'lib/nghttp3_http.c', - 'lib/nghttp3_ksl.c', - 'lib/nghttp3_mem.c', - 'lib/nghttp3_qpack.c', - 'lib/nghttp3_qpack_huffman_data.c', - 'lib/nghttp3_rcbuf.c', - 'lib/nghttp3_str.c', - 'lib/nghttp3_tnode.c', - 'lib/nghttp3_version.c' - ] - } - ] -} diff --git a/deps/ngtcp2/.gitignore b/deps/ngtcp2/.gitignore deleted file mode 100644 index 118f2396034..00000000000 --- a/deps/ngtcp2/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -**/*/CMakeLists.txt -**/*/*.am -**/*/*.h.in -**/*/*.pc.in diff --git a/deps/ngtcp2/COPYING b/deps/ngtcp2/COPYING deleted file mode 100644 index 9b367cdce71..00000000000 --- a/deps/ngtcp2/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2016 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. diff --git a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h deleted file mode 100644 index 4b6885a08e4..00000000000 --- a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +++ /dev/null @@ -1,621 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_H -#define NGTCP2_CRYPTO_H - -#include <ngtcp2/ngtcp2.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32 -#define NGTCP2_CRYPTO_INITIAL_KEYLEN 16 -#define NGTCP2_CRYPTO_INITIAL_IVLEN 12 - -/** - * @function - * - * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet - * encryption and decryption. - */ -NGTCP2_EXTERN ngtcp2_crypto_ctx * -ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); - -/** - * @function - * - * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated - * ciphers and message digests from native TLS session - * |tls_native_handle|. This is used for encrypting/decrypting - * Handshake and Short packets. - * - * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be - * a pointer to SSL object. - */ -NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, - void *tls_native_handle); - -/** - * @function - * - * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher - * AEAD_AES_128_GCM for Retry packet integrity protection. - */ -NGTCP2_EXTERN ngtcp2_crypto_aead * -ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); - -/** - * @function - * - * `ngtcp2_crypto_md_hashlen` returns the length of |md| output. - */ -NGTCP2_EXTERN size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md); - -/** - * @function - * - * `ngtcp2_crypto_aead_keylen` returns the length of key for |aead|. - */ -NGTCP2_EXTERN size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead); - -/** - * @function - * - * `ngtcp2_crypto_aead_noncelen` returns the length of nonce for - * |aead|. - */ -NGTCP2_EXTERN size_t -ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead); - -/** - * @function - * - * `ngtcp2_crypto_aead_taglen` returns the length of tag for |aead|. - */ -NGTCP2_EXTERN size_t ngtcp2_crypto_aead_taglen(const ngtcp2_crypto_aead *aead); - -/** - * @function - * - * `ngtcp2_crypto_hkdf_extract` performs HKDF extract operation. The - * result is the length of |md| and is stored to the buffer pointed by - * |dest|. The caller is responsible to specify the buffer that can - * store the output. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN 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); - -/** - * @function - * - * `ngtcp2_crypto_hkdf_expand` performs HKDF expand operation. The - * result is |destlen| bytes long and is stored to the buffer pointed - * by |dest|. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN 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); - -/** - * @function - * - * `ngtcp2_crypto_hkdf_expand_label` performs HKDF expand label. The - * result is |destlen| bytes long and is stored to the buffer pointed - * by |dest|. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen, - const ngtcp2_crypto_md *md, - const uint8_t *secret, - size_t secretlen, - const uint8_t *label, - size_t labellen); - -/** - * @enum - * - * `ngtcp2_crypto_side` indicates which side the application - * implements; client or server. - */ -typedef enum ngtcp2_crypto_side { - /** - * ``NGTCP2_CRYPTO_SIDE_CLIENT`` indicates that the application is - * client. - */ - NGTCP2_CRYPTO_SIDE_CLIENT, - /** - * ``NGTCP2_CRYPTO_SIDE_SERVER`` indicates that the application is - * server. - */ - NGTCP2_CRYPTO_SIDE_SERVER -} ngtcp2_crypto_side; - -/** - * @function - * - * `ngtcp2_crypto_packet_protection_ivlen` returns the length of IV - * used to encrypt QUIC packet. - */ -NGTCP2_EXTERN size_t -ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead); - -/** - * @function - * - * `ngtcp2_crypto_derive_packet_protection_key` derives packet - * protection key. This function writes packet protection key into - * the buffer pointed by |key|. |key| must point to the buffer which - * is at least ngtcp2_crypto_aead_keylen(aead) bytes long. This - * function writes packet protection IV into |iv|. |iv| must point to - * the buffer which is at least - * ngtcp2_crypto_packet_protection_ivlen(aead). |key| is - * ngtcp2_crypto_aead_keylen(aead) bytes long. |iv| is - * ngtcp2_crypto_packet_protection_ivlen(aead) bytes long. - * - * If |hp| is not NULL, this function also derives packet header - * protection key and writes the key into the buffer pointed by |hp|. - * The length of key is ngtcp2_crypto_aead_keylen(aead) bytes long. - * |hp|, if not NULL, must have enough capacity to store the key. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen); - -/** - * @function - * - * `ngtcp2_crypto_encrypt` encrypts |plaintext| of length - * |plaintextlen| and writes the ciphertext into the buffer pointed by - * |dest|. The length of ciphertext is plaintextlen + - * ngtcp2_crypto_aead_taglen(aead) bytes long. |dest| must have - * enough capacity to store the ciphertext. It is allowed to specify - * the same value to |dest| and |plaintext|. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN 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 *ad, size_t adlen); - -/** - * @function - * - * `ngtcp2_crypto_encrypt_cb` is a wrapper function around - * `ngtcp2_crypto_encrypt`. It can be directly passed to encrypt - * callback to ngtcp2_callbacks. - * - * This function returns 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. - */ -NGTCP2_EXTERN int -ngtcp2_crypto_encrypt_cb(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 *ad, size_t adlen); - -/** - * @function - * - * `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length - * |ciphertextlen| and writes the plaintext into the buffer pointed by - * |dest|. The length of plaintext is ciphertextlen - - * ngtcp2_crypto_aead_taglen(aead) bytes long. |dest| must have enough - * capacity to store the plaintext. It is allowed to specify the same - * value to |dest| and |ciphertext|. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN 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 *ad, size_t adlen); - -/** - * @function - * - * `ngtcp2_crypto_decrypt_cb` is a wrapper function around - * `ngtcp2_crypto_decrypt`. It can be directly passed to decrypt - * callback to ngtcp2_callbacks. - * - * This function returns 0 if it succeeds, or - * :enum:`NGTCP2_ERR_TLS_DECRYPT`. - */ -NGTCP2_EXTERN int -ngtcp2_crypto_decrypt_cb(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 *ad, size_t adlen); - -/** - * @function - * - * `ngtcp2_crypto_hp_mask` generates mask which is used in packet - * header encryption. The mask is written to the buffer pointed by - * |dest|. The length of mask is 5 bytes. |dest| must have enough - * capacity to store the mask. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest, - const ngtcp2_crypto_cipher *hp, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - const uint8_t *sample); - -/** - * @function - * - * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around - * `ngtcp2_crypto_hp_mask`. It can be directly passed to hp_mask - * callback to ngtcp2_callbacks. - * - * This function returns 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. - */ -NGTCP2_EXTERN int -ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - const uint8_t *sample); - -/** - * @function - * - * `ngtcp2_crypto_derive_and_install_rx_key` derives the rx keys from - * |secret| and installs new keys to |conn|. - * - * If |key| is not NULL, the derived packet protection key for - * decryption is written to the buffer pointed by |key|. If |iv| is - * not NULL, the derived packet protection IV for decryption is - * written to the buffer pointed by |iv|. If |hp| is not NULL, the - * derived header protection key for decryption is written to the - * buffer pointed by |hp|. - * - * |secretlen| specifies the length of |secret|. - * - * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_crypto_ctx_tls`. - * - * In the first call of this function, it calls - * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message - * digest algorithm. After the successful call of this function, - * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. - * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag - * length. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key( - ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp, - ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen); - -/** - * @function - * - * `ngtcp2_crypto_derive_and_install_tx_key` derives the tx keys from - * |secret| and installs new keys to |conn|. - * - * If |key| is not NULL, the derived packet protection key for - * encryption is written to the buffer pointed by |key|. If |iv| is - * not NULL, the derived packet protection IV for encryption is - * written to the buffer pointed by |iv|. If |hp| is not NULL, the - * derived header protection key for encryption is written to the - * buffer pointed by |hp|. - * - * |secretlen| specifies the length of |secret|. - * - * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_crypto_ctx_tls`. - * - * In the first call of this function, it calls - * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message - * digest algorithm. After the successful call of this function, - * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. - * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag - * length. - * - * If |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a - * remote QUIC transport parameters extension from |tls| and sets it - * to |conn|. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key( - ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp, - ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen); - -/** - * @function - * - * `ngtcp2_crypto_update_key` updates traffic keying materials. - * - * The new traffic secret for decryption is written to the buffer - * pointed by |rx_secret|. The length of secret is |secretlen| bytes, - * and |rx_secret| must point to the buffer which has enough capacity. - * - * The new traffic secret for encryption is written to the buffer - * pointed by |tx_secret|. The length of secret is |secretlen| bytes, - * and |tx_secret| must point to the buffer which has enough capacity. - * - * The derived packet protection key for decryption is written to the - * buffer pointed by |rx_key|. The derived packet protection IV for - * decryption is written to the buffer pointed by |rx_iv|. - * |rx_aead_ctx| must be constructed with |rx_key|. - * - * The derived packet protection key for encryption is written to the - * buffer pointed by |tx_key|. The derived packet protection IV for - * encryption is written to the buffer pointed by |tx_iv|. - * |tx_aead_ctx| must be constructed with |rx_key|. - * - * |current_rx_secret| and |current_tx_secret| are the current traffic - * secrets for decryption and encryption. |secretlen| specifies the - * length of |rx_secret| and |tx_secret|. - * - * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_conn_get_crypto_ctx`. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_update_key( - ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, - ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, - ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv, - const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, - size_t secretlen); - -/** - * @function - * - * `ngtcp2_crypto_update_key_cb` is a wrapper function around - * `ngtcp2_crypto_update_key`. It can be directly passed to - * update_key field in ngtcp2_callbacks. - * - * This function returns 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. - */ -NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb( - ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, - ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, - ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, - const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, - size_t secretlen, void *user_data); - -/** - * @function - * - * `ngtcp2_crypto_client_initial_cb` installs initial secrets and - * encryption keys and sets QUIC transport parameters. - * - * This function can be directly passed to client_initial field in - * ngtcp2_callbacks. It is only used by client. - * - * This function returns 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. - */ -NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, - void *user_data); - -/** - * @function - * - * `ngtcp2_crypto_recv_retry_cb` re-installs initial secrets in - * response to incoming Retry packet. - * - * This function can be directly passed to recv_retry field in - * ngtcp2_callbacks. It is only used by client. - * - * This function returns 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. - */ -NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, - const ngtcp2_pkt_hd *hd, - void *user_data); - -/** - * @function - * - * `ngtcp2_crypto_recv_client_initial_cb` installs initial secrets in - * response to an incoming Initial packet from client, and sets QUIC - * transport parameters. - * - * This function can be directly passed to recv_client_initial field - * in ngtcp2_callbacks. It is only used by server. - * - * This function returns 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. - */ -NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, - const ngtcp2_cid *dcid, - void *user_data); - -/** - * @function - * - * `ngtcp2_crypto_read_write_crypto_data` reads CRYPTO data |data| of - * length |datalen| in encryption level |crypto_level| and may feed - * outgoing CRYPTO data to |conn|. This function can drive handshake. - * This function can be also used after handshake completes. It is - * allowed to call this function with datalen == 0. In this case, no - * additional read operation is done. - * - * This function returns 0 if it succeeds, or a negative error code. - * The generic error code is -1 if a specific error code is not - * suitable. The error codes less than -10000 are specific to - * underlying TLS implementation. For OpenSSL, the error codes are - * defined in ngtcp2_crypto_openssl.h. - */ -NGTCP2_EXTERN int -ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - const uint8_t *data, size_t datalen); - -/** - * @function - * - * `ngtcp2_crypto_generate_stateless_reset_token` generates a - * stateless reset token using HKDF extraction with |md| using the - * given |cid| and static key |secret| as input. The token will be - * written to the buffer pointed by |token| and it must have a - * capacity of at least NGTCP2_STATELESS_RESET_TOKENLEN bytes. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( - uint8_t *token, const ngtcp2_crypto_md *md, const uint8_t *secret, - size_t secretlen, const ngtcp2_cid *cid); - -/** - * @function - * - * `ngtcp2_crypto_write_connection_close` writes Initial packet - * containing CONNECTION_CLOSE with the given |error_code| to the - * buffer pointed by |dest| of length |destlen|. This function is - * designed for server to close connection without committing the - * state when validating Retry token fails. This function must not be - * used by client. The |dcid| must be the Source Connection ID in - * Initial packet from client. The |scid| must be the Destination - * Connection ID in Initial packet from client. |scid| is used to - * derive initial keying materials. - * - * This function wraps around `ngtcp2_pkt_write_connection_close` for - * easier use. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close( - uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code); - -/** - * @function - * - * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer - * pointed by |dest| of length |destlen|. |odcid| specifies Original - * Destination Connection ID. |token| specifies Retry Token, and - * |tokenlen| specifies its length. - * - * This function wraps around `ngtcp2_pkt_write_retry` for easier use. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN ngtcp2_ssize -ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_cid *odcid, - const uint8_t *token, size_t tokenlen); - -/** - * @function - * - * `ngtcp2_crypto_aead_ctx_encrypt_init` initializes |aead_ctx| with - * new AEAD cipher context object for encryption which is constructed - * to use |key| as encryption key. |aead| specifies AEAD cipher to - * use. |noncelen| is the length of nonce. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN 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); - -/** - * @function - * - * `ngtcp2_crypto_aead_ctx_decrypt_init` initializes |aead_ctx| with - * new AEAD cipher context object for decryption which is constructed - * to use |key| as encryption key. |aead| specifies AEAD cipher to - * use. |noncelen| is the length of nonce. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN 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); - -/** - * @function - * - * `ngtcp2_crypto_aead_ctx_free` frees up resources used by - * |aead_ctx|. This function does not free the memory pointed by - * |aead_ctx| itself. - */ -NGTCP2_EXTERN void -ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx); - -/** - * @function - * - * `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|. - * - * This function can be directly passed to delete_crypto_aead_ctx - * field in ngtcp2_callbacks. - */ -NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb( - ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data); - -/** - * @function - * - * `ngtcp2_crypto_delete_crypto_cipher_ctx_cb` deletes the given - * |cipher_ctx|. - * - * This function can be directly passed to delete_crypto_cipher_ctx - * field in ngtcp2_callbacks. - */ -NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( - ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); - -#ifdef __cplusplus -} -#endif - -#endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h deleted file mode 100644 index 7ccb383e3c8..00000000000 --- a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_OPENSSL_H -#define NGTCP2_CRYPTO_OPENSSL_H - -#include <ngtcp2/ngtcp2.h> - -/* OpenSSL specific error codes */ -#define NGTCP2_CRYPTO_ERR_TLS_WANT_X509_LOOKUP -10001 -#define NGTCP2_CRYPTO_ERR_TLS_WANT_CLIENT_HELLO_CB -10002 - -#endif /* NGTCP2_CRYPTO_OPENSSL_H */ diff --git a/deps/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/crypto/openssl/openssl.c deleted file mode 100644 index d1937441efb..00000000000 --- a/deps/ngtcp2/crypto/openssl/openssl.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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 <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <assert.h> - -#include <ngtcp2/ngtcp2_crypto.h> -#include <ngtcp2/ngtcp2_crypto_openssl.h> - -#include <openssl/ssl.h> -#include <openssl/evp.h> -#include <openssl/kdf.h> - -#include "shared.h" - -ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { - ctx->aead.native_handle = (void *)EVP_aes_128_gcm(); - ctx->md.native_handle = (void *)EVP_sha256(); - ctx->hp.native_handle = (void *)EVP_aes_128_ctr(); - ctx->max_encryption = 0; - ctx->max_decryption_failure = 0; - return ctx; -} - -ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { - aead->native_handle = (void *)EVP_aes_128_gcm(); - return aead; -} - -static const EVP_CIPHER *crypto_ssl_get_aead(SSL *ssl) { - switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { - case TLS1_3_CK_AES_128_GCM_SHA256: - return EVP_aes_128_gcm(); - case TLS1_3_CK_AES_256_GCM_SHA384: - return EVP_aes_256_gcm(); - case TLS1_3_CK_CHACHA20_POLY1305_SHA256: - return EVP_chacha20_poly1305(); - case TLS1_3_CK_AES_128_CCM_SHA256: - return EVP_aes_128_ccm(); - default: - return NULL; - } -} - -static uint64_t crypto_ssl_get_aead_max_encryption(SSL *ssl) { - switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { - 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_ssl_get_aead_max_decryption_failure(SSL *ssl) { - switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { - 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_ssl_get_hp(SSL *ssl) { - switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { - case TLS1_3_CK_AES_128_GCM_SHA256: - case TLS1_3_CK_AES_128_CCM_SHA256: - return EVP_aes_128_ctr(); - case TLS1_3_CK_AES_256_GCM_SHA384: - return EVP_aes_256_ctr(); - case TLS1_3_CK_CHACHA20_POLY1305_SHA256: - return EVP_chacha20(); - default: - return NULL; - } -} - -static const EVP_MD *crypto_ssl_get_md(SSL *ssl) { - switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { - case TLS1_3_CK_AES_128_GCM_SHA256: - case TLS1_3_CK_CHACHA20_POLY1305_SHA256: - case TLS1_3_CK_AES_128_CCM_SHA256: - return EVP_sha256(); - case TLS1_3_CK_AES_256_GCM_SHA384: - return EVP_sha384(); - default: - return NULL; - } -} - -ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, - void *tls_native_handle) { - SSL *ssl = tls_native_handle; - ctx->aead.native_handle = (void *)crypto_ssl_get_aead(ssl); - ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl); - ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl); - ctx->max_encryption = crypto_ssl_get_aead_max_encryption(ssl); - ctx->max_decryption_failure = crypto_ssl_get_aead_max_decryption_failure(ssl); - return ctx; -} - -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); -} - -static size_t crypto_aead_taglen(const EVP_CIPHER *aead) { - if (aead == EVP_aes_128_gcm() || aead == EVP_aes_256_gcm()) { - return EVP_GCM_TLS_TAG_LEN; - } - if (aead == EVP_chacha20_poly1305()) { - return EVP_CHACHAPOLY_TLS_TAG_LEN; - } - if (aead == EVP_aes_128_ccm()) { - return EVP_CCM_TLS_TAG_LEN; - } - return 0; -} - -size_t ngtcp2_crypto_aead_taglen(const ngtcp2_crypto_aead *aead) { - return crypto_aead_taglen(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; - EVP_CIPHER_CTX *actx; - - actx = EVP_CIPHER_CTX_new(); - if (actx == NULL) { - return -1; - } - - if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, - NULL) || - (cipher == EVP_aes_128_ccm() && - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, - (int)crypto_aead_taglen(cipher), NULL)) || - !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; - EVP_CIPHER_CTX *actx; - - actx = EVP_CIPHER_CTX_new(); - if (actx == NULL) { - return -1; - } - - if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, - NULL) || - (cipher == EVP_aes_128_ccm() && - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, - (int)crypto_aead_taglen(cipher), NULL)) || - !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; - int rv = 0; - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); - size_t destlen = (size_t)EVP_MD_size(prf); - - if (pctx == NULL) { - return -1; - } - - if (EVP_PKEY_derive_init(pctx) != 1 || - EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1 || - EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || - EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 || - EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || - EVP_PKEY_derive(pctx, dest, &destlen) != 1) { - rv = -1; - } - - EVP_PKEY_CTX_free(pctx); - - 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; - int rv = 0; - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); - if (pctx == NULL) { - return -1; - } - - if (EVP_PKEY_derive_init(pctx) != 1 || - EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 || - EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || - EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1 || - EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || - EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 || - EVP_PKEY_derive(pctx, dest, &destlen) != 1) { - rv = -1; - } - - EVP_PKEY_CTX_free(pctx); - - 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 *ad, size_t adlen) { - const EVP_CIPHER *cipher = aead->native_handle; - size_t taglen = crypto_aead_taglen(cipher); - EVP_CIPHER_CTX *actx = aead_ctx->native_handle; - int len; - - (void)noncelen; - - if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) || - (cipher == EVP_aes_128_ccm() && - !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || - !EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) || - !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || - !EVP_EncryptFinal_ex(actx, dest + len, &len) || - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen, - dest + plaintextlen)) { - 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 *ad, size_t adlen) { - const EVP_CIPHER *cipher = aead->native_handle; - size_t taglen = crypto_aead_taglen(cipher); - EVP_CIPHER_CTX *actx = aead_ctx->native_handle; - int len; - const uint8_t *tag; - - (void)noncelen; - - if (taglen > ciphertextlen) { - return -1; - } - - ciphertextlen -= taglen; - tag = ciphertext + ciphertextlen; - - if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) || - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, - (uint8_t *)tag) || - (cipher == EVP_aes_128_ccm() && - !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || - !EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) || - !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || - (cipher != EVP_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; -} - -static OSSL_ENCRYPTION_LEVEL -from_ngtcp2_level(ngtcp2_crypto_level crypto_level) { - switch (crypto_level) { - case NGTCP2_CRYPTO_LEVEL_INITIAL: - return ssl_encryption_initial; - case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - return ssl_encryption_handshake; - case NGTCP2_CRYPTO_LEVEL_APP: - return ssl_encryption_application; - case NGTCP2_CRYPTO_LEVEL_EARLY: - return ssl_encryption_early_data; - default: - assert(0); - } -} - -int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - const uint8_t *data, size_t datalen) { - SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn); - int rv; - int err; - - if (SSL_provide_quic_data(ssl, from_ngtcp2_level(crypto_level), data, - datalen) != 1) { - 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_ERR_TLS_WANT_CLIENT_HELLO_CB; - case SSL_ERROR_WANT_X509_LOOKUP: - return NGTCP2_CRYPTO_ERR_TLS_WANT_X509_LOOKUP; - case SSL_ERROR_SSL: - return -1; - default: - return -1; - } - } - - ngtcp2_conn_handshake_completed(conn); - } - - rv = SSL_process_quic_post_handshake(ssl); - 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) { - SSL *ssl = tls; - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO - : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS; - const uint8_t *tp; - size_t tplen; - ngtcp2_transport_params params; - int rv; - - SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); - - rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); - if (rv != 0) { - ngtcp2_conn_set_tls_error(conn, rv); - return -1; - } - - rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); - if (rv != 0) { - ngtcp2_conn_set_tls_error(conn, rv); - return -1; - } - - return 0; -} - -int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, - size_t len) { - if (SSL_set_quic_transport_params(tls, buf, len) != 1) { - return -1; - } - - return 0; -} diff --git a/deps/ngtcp2/crypto/shared.c b/deps/ngtcp2/crypto/shared.c deleted file mode 100644 index 7e5219c52f2..00000000000 --- a/deps/ngtcp2/crypto/shared.c +++ /dev/null @@ -1,786 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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 "shared.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_macro.h" - -int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen, - const ngtcp2_crypto_md *md, - const uint8_t *secret, size_t secretlen, - const uint8_t *label, size_t labellen) { - static const uint8_t LABEL[] = "tls13 "; - uint8_t info[256]; - uint8_t *p = info; - - *p++ = (uint8_t)(destlen / 256); - *p++ = (uint8_t)(destlen % 256); - *p++ = (uint8_t)(sizeof(LABEL) - 1 + labellen); - memcpy(p, LABEL, sizeof(LABEL) - 1); - p += sizeof(LABEL) - 1; - memcpy(p, label, labellen); - p += labellen; - *p++ = 0; - - return ngtcp2_crypto_hkdf_expand(dest, destlen, md, secret, secretlen, info, - (size_t)(p - info)); -} - -#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32 - -int ngtcp2_crypto_derive_initial_secrets(uint8_t *rx_secret, uint8_t *tx_secret, - uint8_t *initial_secret, - const ngtcp2_cid *client_dcid, - ngtcp2_crypto_side side) { - static const uint8_t CLABEL[] = "client in"; - static const uint8_t SLABEL[] = "server in"; - uint8_t initial_secret_buf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; - uint8_t *client_secret; - uint8_t *server_secret; - ngtcp2_crypto_ctx ctx; - - if (!initial_secret) { - initial_secret = initial_secret_buf; - } - - ngtcp2_crypto_ctx_initial(&ctx); - - if (ngtcp2_crypto_hkdf_extract(initial_secret, &ctx.md, client_dcid->data, - client_dcid->datalen, - (const uint8_t *)NGTCP2_INITIAL_SALT, - sizeof(NGTCP2_INITIAL_SALT) - 1) != 0) { - return -1; - } - - if (side == NGTCP2_CRYPTO_SIDE_SERVER) { - client_secret = rx_secret; - server_secret = tx_secret; - } else { - client_secret = tx_secret; - server_secret = rx_secret; - } - - if (ngtcp2_crypto_hkdf_expand_label( - client_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, - initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL, - sizeof(CLABEL) - 1) != 0 || - ngtcp2_crypto_hkdf_expand_label( - server_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, - initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL, - sizeof(SLABEL) - 1) != 0) { - return -1; - } - - return 0; -} - -size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) { - size_t noncelen = ngtcp2_crypto_aead_noncelen(aead); - return ngtcp2_max(8, noncelen); -} - -int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) { - static const uint8_t KEY_LABEL[] = "quic key"; - static const uint8_t IV_LABEL[] = "quic iv"; - static const uint8_t HP_KEY_LABEL[] = "quic hp"; - size_t keylen = ngtcp2_crypto_aead_keylen(aead); - size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - - if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen, - KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) { - return -1; - } - - if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen, - IV_LABEL, sizeof(IV_LABEL) - 1) != 0) { - return -1; - } - - if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label( - hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL, - sizeof(HP_KEY_LABEL) - 1) != 0) { - return -1; - } - - return 0; -} - -int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, - const ngtcp2_crypto_md *md, - const uint8_t *secret, - size_t secretlen) { - static const uint8_t LABEL[] = "quic ku"; - - if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen, - LABEL, sizeof(LABEL) - 1) != 0) { - return -1; - } - - return 0; -} - -int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, - uint8_t *iv, uint8_t *hp_key, - ngtcp2_crypto_level level, - const uint8_t *secret, - size_t secretlen) { - const ngtcp2_crypto_ctx *ctx; - const ngtcp2_crypto_aead *aead; - const ngtcp2_crypto_md *md; - const ngtcp2_crypto_cipher *hp; - ngtcp2_crypto_aead_ctx aead_ctx = {0}; - ngtcp2_crypto_cipher_ctx hp_ctx = {0}; - void *tls = ngtcp2_conn_get_tls_native_handle(conn); - uint8_t keybuf[64], ivbuf[64], hp_keybuf[64]; - size_t ivlen; - int rv; - - if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) { - return 0; - } - - if (!key) { - key = keybuf; - } - if (!iv) { - iv = ivbuf; - } - if (!hp_key) { - hp_key = hp_keybuf; - } - - ctx = ngtcp2_conn_get_crypto_ctx(conn); - - if (!ctx->aead.native_handle) { - ngtcp2_crypto_ctx cctx; - ngtcp2_crypto_ctx_tls(&cctx, tls); - ngtcp2_conn_set_aead_overhead(conn, ngtcp2_crypto_aead_taglen(&cctx.aead)); - ngtcp2_conn_set_crypto_ctx(conn, &cctx); - ctx = ngtcp2_conn_get_crypto_ctx(conn); - } - - aead = &ctx->aead; - md = &ctx->md; - hp = &ctx->hp; - ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { - return -1; - } - - if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, aead, key, ivlen) != 0) { - goto fail; - } - - if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) { - goto fail; - } - - switch (level) { - case NGTCP2_CRYPTO_LEVEL_EARLY: - rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); - if (rv != 0) { - goto fail; - } - break; - case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, iv, ivlen, - &hp_ctx); - if (rv != 0) { - goto fail; - } - break; - case NGTCP2_CRYPTO_LEVEL_APP: - if (!ngtcp2_conn_is_server(conn)) { - rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); - if (rv != 0) { - goto fail; - } - } - - rv = ngtcp2_conn_install_rx_key(conn, secret, secretlen, &aead_ctx, iv, - ivlen, &hp_ctx); - if (rv != 0) { - goto fail; - } - - break; - default: - goto fail; - } - - return 0; - -fail: - ngtcp2_crypto_cipher_ctx_free(&hp_ctx); - ngtcp2_crypto_aead_ctx_free(&aead_ctx); - - return -1; -} - -/* - * crypto_set_local_transport_params gets local QUIC transport - * parameters from |conn| and sets it to |tls|. - * - * This function returns 0 if it succeeds, or -1. - */ -static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) { - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS - : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO; - ngtcp2_transport_params params; - ngtcp2_ssize nwrite; - uint8_t buf[256]; - - ngtcp2_conn_get_local_transport_params(conn, ¶ms); - - nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, ¶ms); - if (nwrite < 0) { - return -1; - } - - if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) { - return -1; - } - - return 0; -} - -int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, - uint8_t *iv, uint8_t *hp_key, - ngtcp2_crypto_level level, - const uint8_t *secret, - size_t secretlen) { - const ngtcp2_crypto_ctx *ctx; - const ngtcp2_crypto_aead *aead; - const ngtcp2_crypto_md *md; - const ngtcp2_crypto_cipher *hp; - ngtcp2_crypto_aead_ctx aead_ctx = {0}; - ngtcp2_crypto_cipher_ctx hp_ctx = {0}; - void *tls = ngtcp2_conn_get_tls_native_handle(conn); - uint8_t keybuf[64], ivbuf[64], hp_keybuf[64]; - size_t ivlen; - int rv; - - if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) { - return 0; - } - - if (!key) { - key = keybuf; - } - if (!iv) { - iv = ivbuf; - } - if (!hp_key) { - hp_key = hp_keybuf; - } - - ctx = ngtcp2_conn_get_crypto_ctx(conn); - - if (!ctx->aead.native_handle) { - ngtcp2_crypto_ctx cctx; - ngtcp2_crypto_ctx_tls(&cctx, tls); - ngtcp2_conn_set_aead_overhead(conn, ngtcp2_crypto_aead_taglen(&cctx.aead)); - ngtcp2_conn_set_crypto_ctx(conn, &cctx); - ctx = ngtcp2_conn_get_crypto_ctx(conn); - } - - aead = &ctx->aead; - md = &ctx->md; - hp = &ctx->hp; - ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { - return -1; - } - - if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, aead, key, ivlen) != 0) { - goto fail; - } - - if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) { - goto fail; - } - - switch (level) { - case NGTCP2_CRYPTO_LEVEL_EARLY: - rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); - if (rv != 0) { - goto fail; - } - break; - case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen, - &hp_ctx); - if (rv != 0) { - goto fail; - } - - if (ngtcp2_conn_is_server(conn)) { - rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); - if (rv != 0) { - goto fail; - } - - if (crypto_set_local_transport_params(conn, tls) != 0) { - goto fail; - } - } - - break; - case NGTCP2_CRYPTO_LEVEL_APP: - rv = ngtcp2_conn_install_tx_key(conn, secret, secretlen, &aead_ctx, iv, - ivlen, &hp_ctx); - if (rv != 0) { - goto fail; - } - - break; - default: - goto fail; - } - - return 0; - -fail: - ngtcp2_crypto_cipher_ctx_free(&hp_ctx); - ngtcp2_crypto_aead_ctx_free(&aead_ctx); - - return -1; -} - -int ngtcp2_crypto_derive_and_install_initial_key( - ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, - uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, - uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, - const ngtcp2_cid *client_dcid) { - uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; - uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; - uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; - uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; - uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; - uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; - uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; - uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; - uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; - ngtcp2_crypto_ctx ctx; - ngtcp2_crypto_aead retry_aead; - ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}; - ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0}; - ngtcp2_crypto_aead_ctx tx_aead_ctx = {0}; - ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0}; - ngtcp2_crypto_aead_ctx retry_aead_ctx = {0}; - int rv; - int server = ngtcp2_conn_is_server(conn); - - ngtcp2_crypto_ctx_initial(&ctx); - - if (!rx_secret) { - rx_secret = rx_secretbuf; - } - if (!tx_secret) { - tx_secret = tx_secretbuf; - } - if (!initial_secret) { - initial_secret = initial_secretbuf; - } - - if (!rx_key) { - rx_key = rx_keybuf; - } - if (!rx_iv) { - rx_iv = rx_ivbuf; - } - if (!rx_hp_key) { - rx_hp_key = rx_hp_keybuf; - } - if (!tx_key) { - tx_key = tx_keybuf; - } - if (!tx_iv) { - tx_iv = tx_ivbuf; - } - if (!tx_hp_key) { - tx_hp_key = tx_hp_keybuf; - } - - ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx); - - if (ngtcp2_crypto_derive_initial_secrets( - rx_secret, tx_secret, initial_secret, client_dcid, - server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) != - 0) { - return -1; - } - - if (ngtcp2_crypto_derive_packet_protection_key( - rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret, - NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { - return -1; - } - - if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, - NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { - return -1; - } - - if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx.aead, rx_key, - NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { - goto fail; - } - - if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx.hp, rx_hp_key) != - 0) { - goto fail; - } - - if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx.aead, tx_key, - NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { - goto fail; - } - - if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx.hp, tx_hp_key) != - 0) { - goto fail; - } - - if (!server && !ngtcp2_conn_after_retry(conn)) { - ngtcp2_crypto_aead_retry(&retry_aead); - - if (ngtcp2_crypto_aead_ctx_encrypt_init( - &retry_aead_ctx, &retry_aead, (const uint8_t *)NGTCP2_RETRY_KEY, - sizeof(NGTCP2_RETRY_NONCE) - 1) != 0) { - goto fail; - } - } - - rv = ngtcp2_conn_install_initial_key(conn, &rx_aead_ctx, rx_iv, &rx_hp_ctx, - &tx_aead_ctx, tx_iv, &tx_hp_ctx, - NGTCP2_CRYPTO_INITIAL_IVLEN); - if (rv != 0) { - goto fail; - } - - if (retry_aead_ctx.native_handle) { - ngtcp2_conn_set_retry_aead(conn, &retry_aead, &retry_aead_ctx); - } - - return 0; - -fail: - ngtcp2_crypto_aead_ctx_free(&retry_aead_ctx); - ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx); - ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx); - ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx); - ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx); - - return -1; -} - -int ngtcp2_crypto_update_key( - ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, - ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, - ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv, - const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, - size_t secretlen) { - const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_crypto_ctx(conn); - const ngtcp2_crypto_aead *aead = &ctx->aead; - const ngtcp2_crypto_md *md = &ctx->md; - size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - - if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret, - secretlen) != 0) { - return -1; - } - - if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md, - rx_secret, secretlen) != 0) { - return -1; - } - - if (ngtcp2_crypto_update_traffic_secret(tx_secret, md, current_tx_secret, - secretlen) != 0) { - return -1; - } - - if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md, - tx_secret, secretlen) != 0) { - return -1; - } - - if (ngtcp2_crypto_aead_ctx_decrypt_init(rx_aead_ctx, aead, rx_key, ivlen) != - 0) { - return -1; - } - - if (ngtcp2_crypto_aead_ctx_encrypt_init(tx_aead_ctx, aead, tx_key, ivlen) != - 0) { - ngtcp2_crypto_aead_ctx_free(rx_aead_ctx); - rx_aead_ctx->native_handle = NULL; - return -1; - } - - return 0; -} - -int ngtcp2_crypto_encrypt_cb(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 *ad, size_t adlen) { - if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen, - nonce, noncelen, ad, adlen) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -int ngtcp2_crypto_decrypt_cb(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 *ad, size_t adlen) { - if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen, - nonce, noncelen, ad, adlen) != 0) { - return NGTCP2_ERR_TLS_DECRYPT; - } - return 0; -} - -int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - const uint8_t *sample) { - if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -int ngtcp2_crypto_update_key_cb( - ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, - ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, - ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, - const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, - size_t secretlen, void *user_data) { - uint8_t rx_key[64]; - uint8_t tx_key[64]; - (void)conn; - (void)user_data; - - if (ngtcp2_crypto_update_key(conn, rx_secret, tx_secret, rx_aead_ctx, rx_key, - rx_iv, tx_aead_ctx, tx_key, tx_iv, - current_rx_secret, current_tx_secret, - secretlen) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token, - const ngtcp2_crypto_md *md, - const uint8_t *secret, - size_t secretlen, - const ngtcp2_cid *cid) { - uint8_t buf[64]; - int rv; - - assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf)); - assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf)); - - rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data, - cid->datalen); - if (rv != 0) { - return -1; - } - - memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN); - - return 0; -} - -ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, - const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, - uint64_t error_code) { - uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; - uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; - uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; - uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; - uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; - uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; - ngtcp2_crypto_ctx ctx; - ngtcp2_ssize spktlen; - ngtcp2_crypto_aead_ctx aead_ctx = {0}; - ngtcp2_crypto_cipher_ctx hp_ctx = {0}; - - ngtcp2_crypto_ctx_initial(&ctx); - - if (ngtcp2_crypto_derive_initial_secrets(rx_secret, tx_secret, initial_secret, - scid, - NGTCP2_CRYPTO_SIDE_SERVER) != 0) { - return -1; - } - - if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, - NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { - return -1; - } - - if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &ctx.aead, tx_key, - NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { - spktlen = -1; - goto end; - } - - if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, &ctx.hp, tx_hp_key) != 0) { - spktlen = -1; - goto end; - } - - spktlen = ngtcp2_pkt_write_connection_close( - dest, destlen, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb, - &ctx.aead, &aead_ctx, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx); - if (spktlen < 0) { - spktlen = -1; - } - -end: - ngtcp2_crypto_cipher_ctx_free(&hp_ctx); - ngtcp2_crypto_aead_ctx_free(&aead_ctx); - - return spktlen; -} - -ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, - const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, - const ngtcp2_cid *odcid, - const uint8_t *token, size_t tokenlen) { - ngtcp2_crypto_aead aead; - ngtcp2_ssize spktlen; - ngtcp2_crypto_aead_ctx aead_ctx = {0}; - - ngtcp2_crypto_aead_retry(&aead); - - if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, - (const uint8_t *)NGTCP2_RETRY_KEY, - NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { - return -1; - } - - spktlen = - ngtcp2_pkt_write_retry(dest, destlen, dcid, scid, odcid, token, tokenlen, - ngtcp2_crypto_encrypt_cb, &aead, &aead_ctx); - if (spktlen < 0) { - spktlen = -1; - } - - ngtcp2_crypto_aead_ctx_free(&aead_ctx); - - return spktlen; -} - -/* - * crypto_setup_initial_crypto establishes the initial secrets and - * encryption keys, and prepares local QUIC transport parameters. - */ -static int crypto_setup_initial_crypto(ngtcp2_conn *conn, - const ngtcp2_cid *dcid) { - return ngtcp2_crypto_derive_and_install_initial_key( - conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid); -} - -int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) { - const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn); - void *tls = ngtcp2_conn_get_tls_native_handle(conn); - (void)user_data; - - if (crypto_setup_initial_crypto(conn, dcid) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - if (crypto_set_local_transport_params(conn, tls) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - if (ngtcp2_crypto_read_write_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, - NULL, 0) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - void *user_data) { - (void)user_data; - - if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &hd->scid) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, - const ngtcp2_cid *dcid, - void *user_data) { - (void)user_data; - - if (crypto_setup_initial_crypto(conn, dcid) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -void ngtcp2_crypto_delete_crypto_aead_ctx_cb(ngtcp2_conn *conn, - ngtcp2_crypto_aead_ctx *aead_ctx, - void *user_data) { - (void)conn; - (void)user_data; - - ngtcp2_crypto_aead_ctx_free(aead_ctx); -} - -void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( - ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data) { - (void)conn; - (void)user_data; - - ngtcp2_crypto_cipher_ctx_free(cipher_ctx); -} diff --git a/deps/ngtcp2/crypto/shared.h b/deps/ngtcp2/crypto/shared.h deleted file mode 100644 index b70513afb3a..00000000000 --- a/deps/ngtcp2/crypto/shared.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_SHARED_H -#define NGTCP2_SHARED_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2_crypto.h> - -/* Maximum key usage (encryption) limits */ -#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (23726566ULL) -#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62) -#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM (1ULL << 23) - -/* Maximum authentication failure (decryption) limits */ -#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM (1ULL << 36) -#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36) -#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (11863283ULL) - -/** - * @function - * - * `ngtcp2_crypto_derive_initial_secrets` derives initial secrets. - * |rx_secret| and |tx_secret| must point to the buffer of at least 32 - * bytes capacity. rx for read and tx for write. This function - * writes rx and tx secrets into |rx_secret| and |tx_secret| - * respectively. The length of secret is 32 bytes long. - * |client_dcid| is the destination connection ID in first Initial - * packet of client. If |initial_secret| is not NULL, the initial - * secret is written to it. It must point to the buffer which has at - * least 32 bytes capacity. The initial secret is 32 bytes long. - * |side| specifies the side of application. - * - * This function returns 0 if it succeeds, or -1. - */ -int ngtcp2_crypto_derive_initial_secrets(uint8_t *rx_secret, uint8_t *tx_secret, - uint8_t *initial_secret, - const ngtcp2_cid *client_dcid, - ngtcp2_crypto_side side); - -/** - * @function - * - * `ngtcp2_crypto_update_traffic_secret` derives the next generation - * of the traffic secret. |secret| specifies the current secret and - * its length is given in |secretlen|. The length of new key is the - * same as the current key. This function writes new key into the - * buffer pointed by |dest|. |dest| must have the enough capacity to - * store the new key. - * - * This function returns 0 if it succeeds, or -1. - */ -int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, - const ngtcp2_crypto_md *md, - const uint8_t *secret, - size_t secretlen); - -/** - * @function - * - * `ngtcp2_crypto_set_local_transport_params` sets QUIC transport - * parameter, which is encoded in wire format and stored in the buffer - * pointed by |buf| of length |len|, to the native handle |tls|. - * - * |tls| points to a implementation dependent TLS session object. If - * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL - * object. - * - * This function returns 0 if it succeeds, or -1. - */ -int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, - size_t len); - -/** - * @function - * - * `ngtcp2_crypto_set_remote_transport_params` retrieves a remote QUIC - * transport parameters from |tls| and sets it to |conn| using - * `ngtcp2_conn_set_remote_transport_params`. - * - * |tls| points to a implementation dependent TLS session object. If - * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL - * object. - * - * This function returns 0 if it succeeds, or -1. - */ -int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls); - -/** - * @function - * - * `ngtcp2_crypto_derive_and_install_initial_key` derives initial - * keying materials and installs keys to |conn|. - * - * If |rx_secret| is not NULL, the secret for decryption is written to - * the buffer pointed by |rx_secret|. The length of secret is 32 - * bytes, and |rx_secret| must point to the buffer which has enough - * capacity. - * - * If |tx_secret| is not NULL, the secret for encryption is written to - * the buffer pointed by |tx_secret|. The length of secret is 32 - * bytes, and |tx_secret| must point to the buffer which has enough - * capacity. - * - * If |initial_secret| is not NULL, the initial secret is written to - * the buffer pointed by |initial_secret|. The length of secret is 32 - * bytes, and |initial_secret| must point to the buffer which has - * enough capacity. - * - * |client_dcid| is the destination connection ID in first Initial - * packet of client. - * - * If |rx_key| is not NULL, the derived packet protection key for - * decryption is written to the buffer pointed by |rx_key|. If - * |rx_iv| is not NULL, the derived packet protection IV for - * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp| - * is not NULL, the derived header protection key for decryption is - * written to the buffer pointed by |rx_hp|. - * - * If |tx_key| is not NULL, the derived packet protection key for - * encryption is written to the buffer pointed by |tx_key|. If - * |tx_iv| is not NULL, the derived packet protection IV for - * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp| - * is not NULL, the derived header protection key for encryption is - * written to the buffer pointed by |tx_hp|. - * - * The length of packet protection key and header protection key is 16 - * bytes long. The length of packet protection IV is 12 bytes long. - * - * This function calls `ngtcp2_conn_set_initial_crypto_ctx` to set - * initial AEAD and message digest algorithm. After the successful - * call of this function, application can use - * `ngtcp2_conn_get_initial_crypto_ctx` to get the object. - * - * This function returns 0 if it succeeds, or -1. - */ -int ngtcp2_crypto_derive_and_install_initial_key( - ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, - uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, - uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, - const ngtcp2_cid *client_dcid); - -/** - * @function - * - * `ngtcp2_crypto_cipher_ctx_encrypt_init` initializes |cipher_ctx| - * with new cipher context object for encryption which is constructed - * to use |key| as encryption key. |cipher| specifies cipher to use. - * - * This function returns 0 if it succeeds, or -1. - */ -int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, - const ngtcp2_crypto_cipher *cipher, - const uint8_t *key); - -/** - * @function - * - * `ngtcp2_crypto_cipher_ctx_free` frees up resources used by - * |cipher_ctx|. This function does not free the memory pointed by - * |cipher_ctx| itself. - */ -void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx); - -#endif /* NGTCP2_SHARED_H */ diff --git a/deps/ngtcp2/lib/includes/config.h b/deps/ngtcp2/lib/includes/config.h deleted file mode 100644 index 0aee7749bae..00000000000 --- a/deps/ngtcp2/lib/includes/config.h +++ /dev/null @@ -1,39 +0,0 @@ - -/* Edited to match src/node.h. */ -#include <stdint.h> - -#ifdef _WIN32 -#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) -typedef intptr_t ssize_t; -# define _SSIZE_T_ -# define _SSIZE_T_DEFINED -#endif -#else // !_WIN32 -# include <sys/types.h> // size_t, ssize_t -#endif // _WIN32 - -#ifdef _MSC_VER -# include <intrin.h> -# define __builtin_popcount __popcnt -#endif - -/* Define to 1 to enable debug output. */ -/* #undef DEBUGBUILD */ - -/* Define to 1 if you have the <arpa/inet.h> header file. */ -/* #undef HAVE_ARPA_INET_H */ - -/* Define to 1 if you have the <stddef.h> header file. */ -#define HAVE_STDDEF_H 1 - -/* Define to 1 if you have the <stdint.h> header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the <string.h> header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the <unistd.h> header file. */ -/* #undef HAVE_UNISTD_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h deleted file mode 100644 index 3373adb9f91..00000000000 --- a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ /dev/null @@ -1,3396 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2017 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. - */ -#ifndef NGTCP2_H -#define NGTCP2_H - -/* Define WIN32 when build target is Win32 API (borrowed from - libcurl) */ -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -# define WIN32 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdlib.h> -#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 <stdint.h> -#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ -# include <inttypes.h> -#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ -#include <sys/types.h> -#include <stdarg.h> -#include <stddef.h> - -#ifdef WIN32 -# include <winsock2.h> -#else -# include <sys/socket.h> -#endif - -#include <ngtcp2/version.h> - -#ifdef NGTCP2_STATICLIB -# define NGTCP2_EXTERN -#elif defined(WIN32) -# ifdef BUILDING_NGTCP2 -# define NGTCP2_EXTERN __declspec(dllexport) -# else /* !BUILDING_NGTCP2 */ -# define NGTCP2_EXTERN __declspec(dllimport) -# endif /* !BUILDING_NGTCP2 */ -#else /* !defined(WIN32) */ -# ifdef BUILDING_NGTCP2 -# define NGTCP2_EXTERN __attribute__((visibility("default"))) -# else /* !BUILDING_NGTCP2 */ -# define NGTCP2_EXTERN -# endif /* !BUILDING_NGTCP2 */ -#endif /* !defined(WIN32) */ - -typedef ptrdiff_t ngtcp2_ssize; - -/** - * @functypedef - * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. - */ -typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`ngtcp2_mem` structure. - */ -typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. - */ -typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. - */ -typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); - -/** - * @struct - * - * Custom memory allocator functions and user defined pointer. The - * |mem_user_data| member is passed to each allocator function. This - * can be used, for example, to achieve per-session memory pool. - * - * In the following example code, ``my_malloc``, ``my_free``, - * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: - * - * void *my_malloc_cb(size_t size, void *mem_user_data) { - * return my_malloc(size); - * } - * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } - * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { - * return my_calloc(nmemb, size); - * } - * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { - * return my_realloc(ptr, size); - * } - * - * void conn_new() { - * ngtcp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, - * my_realloc_cb}; - * - * ... - * } - */ -typedef struct ngtcp2_mem { - /** - * An arbitrary user supplied data. This is passed to each - * allocator function. - */ - void *mem_user_data; - /** - * Custom allocator function to replace malloc(). - */ - ngtcp2_malloc malloc; - /** - * Custom allocator function to replace free(). - */ - ngtcp2_free free; - /** - * Custom allocator function to replace calloc(). - */ - ngtcp2_calloc calloc; - /** - * Custom allocator function to replace realloc(). - */ - ngtcp2_realloc realloc; -} ngtcp2_mem; - -/* NGTCP2_PROTO_VER is the supported QUIC protocol version. */ -#define NGTCP2_PROTO_VER 0xff00001du -/* NGTCP2_PROTO_VER_MAX is the highest QUIC version the library - supports. */ -#define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER - -#define NGTCP2_MAX_PKTLEN_IPV4 1252 -#define NGTCP2_MAX_PKTLEN_IPV6 1232 - -/* NGTCP2_MIN_INITIAL_PKTLEN is the minimum UDP packet size for a - packet sent by client which contains its first Initial packet. */ -#define NGTCP2_MIN_INITIAL_PKTLEN 1200 - -/* NGTCP2_DEFAULT_MAX_PKTLEN is the default maximum size of UDP - datagram payload that this endpoint transmits. It is used by - congestion controller to compute congestion window. */ -#define NGTCP2_DEFAULT_MAX_PKTLEN 1200 - -/* NGTCP2_STATELESS_RESET_TOKENLEN is the length of Stateless Reset - Token. */ -#define NGTCP2_STATELESS_RESET_TOKENLEN 16 - -/* NGTCP2_MIN_STATELESS_RESET_RANDLEN is the minimum length of random - bytes (Unpredictable Bits) in Stateless Retry packet */ -#define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5 - -/* NGTCP2_INITIAL_SALT is a salt value which is used to derive initial - secret. */ -#define NGTCP2_INITIAL_SALT \ - "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90" \ - "\xa8\x99" - -/* NGTCP2_RETRY_KEY is an encryption key to create integrity tag of - Retry packet. */ -#define NGTCP2_RETRY_KEY \ - "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1" - -/* NGTCP2_RETRY_NONCE is nonce used when generating integrity tag of - Retry packet. */ -#define NGTCP2_RETRY_NONCE "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c" - -/* NGTCP2_HP_MASKLEN is the length of header protection mask. */ -#define NGTCP2_HP_MASKLEN 5 - -/* NGTCP2_HP_SAMPLELEN is the number bytes sampled when encrypting a - packet header. */ -#define NGTCP2_HP_SAMPLELEN 16 - -/* NGTCP2_SECONDS is a count of tick which corresponds to 1 second. */ -#define NGTCP2_SECONDS ((uint64_t)1000000000ULL) - -/* NGTCP2_MILLISECONDS is a count of tick which corresponds to 1 - millisecond. */ -#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL) - -/* NGTCP2_MICROSECONDS is a count of tick which corresponds to 1 - microsecond. */ -#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL) - -/* NGTCP2_NANOSECONDS is a count of tick which corresponds to 1 - nanosecond. */ -#define NGTCP2_NANOSECONDS ((uint64_t)1ULL) - -/* NGTCP2_DEFAULT_INITIAL_RTT is a default initial RTT. */ -#define NGTCP2_DEFAULT_INITIAL_RTT (333 * NGTCP2_MILLISECONDS) - -#if defined(__cplusplus) && __cplusplus >= 201103L -typedef enum ngtcp2_lib_error : int { -#else -typedef enum ngtcp2_lib_error { -#endif - NGTCP2_ERR_INVALID_ARGUMENT = -201, - NGTCP2_ERR_UNKNOWN_PKT_TYPE = -202, - NGTCP2_ERR_NOBUF = -203, - NGTCP2_ERR_PROTO = -205, - NGTCP2_ERR_INVALID_STATE = -206, - NGTCP2_ERR_ACK_FRAME = -207, - NGTCP2_ERR_STREAM_ID_BLOCKED = -208, - NGTCP2_ERR_STREAM_IN_USE = -209, - NGTCP2_ERR_STREAM_DATA_BLOCKED = -210, - NGTCP2_ERR_FLOW_CONTROL = -211, - NGTCP2_ERR_CONNECTION_ID_LIMIT = -212, - NGTCP2_ERR_STREAM_LIMIT = -213, - NGTCP2_ERR_FINAL_SIZE = -214, - NGTCP2_ERR_CRYPTO = -215, - NGTCP2_ERR_PKT_NUM_EXHAUSTED = -216, - NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM = -217, - NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM = -218, - NGTCP2_ERR_FRAME_ENCODING = -219, - NGTCP2_ERR_TLS_DECRYPT = -220, - NGTCP2_ERR_STREAM_SHUT_WR = -221, - NGTCP2_ERR_STREAM_NOT_FOUND = -222, - NGTCP2_ERR_STREAM_STATE = -226, - NGTCP2_ERR_RECV_VERSION_NEGOTIATION = -229, - NGTCP2_ERR_CLOSING = -230, - NGTCP2_ERR_DRAINING = -231, - NGTCP2_ERR_TRANSPORT_PARAM = -234, - NGTCP2_ERR_DISCARD_PKT = -235, - NGTCP2_ERR_PATH_VALIDATION_FAILED = -236, - NGTCP2_ERR_CONN_ID_BLOCKED = -237, - NGTCP2_ERR_INTERNAL = -238, - NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED = -239, - NGTCP2_ERR_WRITE_MORE = -240, - NGTCP2_ERR_RETRY = -241, - NGTCP2_ERR_DROP_CONN = -242, - NGTCP2_ERR_FATAL = -500, - NGTCP2_ERR_NOMEM = -501, - NGTCP2_ERR_CALLBACK_FAILURE = -502, -} ngtcp2_lib_error; - -typedef enum ngtcp2_pkt_flag { - NGTCP2_PKT_FLAG_NONE = 0, - NGTCP2_PKT_FLAG_LONG_FORM = 0x01, - NGTCP2_PKT_FLAG_KEY_PHASE = 0x04 -} ngtcp2_pkt_flag; - -#if defined(__cplusplus) && __cplusplus >= 201103L -typedef enum ngtcp2_pkt_type : int { -#else -typedef enum ngtcp2_pkt_type { -#endif - /* NGTCP2_PKT_VERSION_NEGOTIATION is defined by libngtcp2 for - convenience. */ - NGTCP2_PKT_VERSION_NEGOTIATION = 0xf0, - NGTCP2_PKT_INITIAL = 0x0, - NGTCP2_PKT_0RTT = 0x1, - NGTCP2_PKT_HANDSHAKE = 0x2, - NGTCP2_PKT_RETRY = 0x3, - /* NGTCP2_PKT_SHORT is defined by libngtcp2 for convenience. */ - NGTCP2_PKT_SHORT = 0x70 -} ngtcp2_pkt_type; - -/* QUIC transport error code. */ -#define NGTCP2_NO_ERROR 0x0u -#define NGTCP2_INTERNAL_ERROR 0x1u -#define NGTCP2_CONNECTION_REFUSED 0x2u -#define NGTCP2_FLOW_CONTROL_ERROR 0x3u -#define NGTCP2_STREAM_LIMIT_ERROR 0x4u -#define NGTCP2_STREAM_STATE_ERROR 0x5u -#define NGTCP2_FINAL_SIZE_ERROR 0x6u -#define NGTCP2_FRAME_ENCODING_ERROR 0x7u -#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u -#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u -#define NGTCP2_PROTOCOL_VIOLATION 0xau -#define NGTCP2_INVALID_TOKEN 0xbu -#define NGTCP2_APPLICATION_ERROR 0xcu -#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu -#define NGTCP2_KEY_UPDATE_ERROR 0xeu -#define NGTCP2_CRYPTO_ERROR 0x100u - -#if defined(__cplusplus) && __cplusplus >= 201103L -typedef enum ngtcp2_path_validation_result : int { -#else -typedef enum ngtcp2_path_validation_result { -#endif - NGTCP2_PATH_VALIDATION_RESULT_SUCCESS, - NGTCP2_PATH_VALIDATION_RESULT_FAILURE, -} ngtcp2_path_validation_result; - -/* - * ngtcp2_tstamp is a timestamp with nanosecond resolution. - */ -typedef uint64_t ngtcp2_tstamp; - -/* - * ngtcp2_duration is a period of time in nanosecond resolution. - */ -typedef uint64_t ngtcp2_duration; - -/* NGTCP2_MAX_CIDLEN is the maximum length of Connection ID. */ -#define NGTCP2_MAX_CIDLEN 20 -/* NGTCP2_MIN_CIDLEN is the minimum length of Connection ID. */ -#define NGTCP2_MIN_CIDLEN 1 - -/* NGTCP2_MIN_INITIAL_DCIDLEN is the minimum length of Destination - Connection ID in Client Initial packet if it does not bear token - from Retry packet. */ -#define NGTCP2_MIN_INITIAL_DCIDLEN 8 - -/** - * @struct - * - * ngtcp2_cid holds a Connection ID. - */ -typedef struct ngtcp2_cid { - size_t datalen; - uint8_t data[NGTCP2_MAX_CIDLEN]; -} ngtcp2_cid; - -/** - * @struct - * - * ngtcp2_vec is struct iovec compatible structure to reference - * arbitrary array of bytes. - */ -typedef struct ngtcp2_vec { - /* base points to the data. */ - uint8_t *base; - /* len is the number of bytes which the buffer pointed by base - contains. */ - size_t len; -} ngtcp2_vec; - -/** - * @function - * - * `ngtcp2_cid_init` initializes Connection ID |cid| with the byte - * string pointed by |data| and its length is |datalen|. |datalen| - * must be at least :enum:`NGTCP2_MIN_CIDLEN`, and at most - * :enum:`NGTCP2_MAX_CIDLEN`. - */ -NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, - size_t datalen); - -typedef struct ngtcp2_pkt_hd { - ngtcp2_cid dcid; - ngtcp2_cid scid; - int64_t pkt_num; - ngtcp2_vec token; - /** - * pkt_numlen is the number of bytes spent to encode pkt_num. - */ - size_t pkt_numlen; - /** - * len is the sum of pkt_numlen and the length of QUIC packet - * payload. - */ - size_t len; - uint32_t version; - uint8_t type; - uint8_t flags; -} ngtcp2_pkt_hd; - -typedef struct ngtcp2_pkt_stateless_reset { - uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; - const uint8_t *rand; - size_t randlen; -} ngtcp2_pkt_stateless_reset; - -#if defined(__cplusplus) && __cplusplus >= 201103L -typedef enum ngtcp2_transport_param_id : int { -#else -typedef enum ngtcp2_transport_param_id { -#endif - NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000, - NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, - NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, - NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, - NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, - NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, - NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, - NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, - NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, - NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, - NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010 -} ngtcp2_transport_param_id; - -#if defined(__cplusplus) && __cplusplus >= 201103L -typedef enum ngtcp2_transport_params_type : int { -#else -typedef enum ngtcp2_transport_params_type { -#endif - NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, - NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS -} ngtcp2_transport_params_type; - -/** - * @enum - * - * ngtcp2_rand_ctx is a context where generated random value is used. - */ -#if defined(__cplusplus) && __cplusplus >= 201103L -typedef enum ngtcp2_rand_ctx : int { -#else -typedef enum ngtcp2_rand_ctx { -#endif - NGTCP2_RAND_CTX_NONE, - /** - * NGTCP2_RAND_CTX_PATH_CHALLENGE indicates that random value is - * used for PATH_CHALLENGE. - */ - NGTCP2_RAND_CTX_PATH_CHALLENGE -} ngtcp2_rand_ctx; - -/* - * NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE is the default value of - * max_udp_payload_size transport parameter. - */ -#define NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE 65527 - -/** - * @macro - * - * NGTCP2_DEFAULT_ACK_DELAY_EXPONENT is a default value of scaling - * factor of ACK Delay field in ACK frame. - */ -#define NGTCP2_DEFAULT_ACK_DELAY_EXPONENT 3 - -/** - * @macro - * - * NGTCP2_DEFAULT_MAX_ACK_DELAY is a default value of the maximum - * amount of time in nanoseconds by which endpoint delays sending - * acknowledgement. - */ -#define NGTCP2_DEFAULT_MAX_ACK_DELAY (25 * NGTCP2_MILLISECONDS) - -/** - * @macro - * - * NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT is the default value of - * active_connection_id_limit transport parameter value if omitted. - */ -#define NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT 2 - -/** - * @macro - * - * NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS is TLS extension type of - * quic_transport_parameters. - */ -#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS 0xffa5u - -typedef struct ngtcp2_preferred_addr { - ngtcp2_cid cid; - uint16_t ipv4_port; - uint16_t ipv6_port; - uint8_t ipv4_addr[4]; - uint8_t ipv6_addr[16]; - uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; -} ngtcp2_preferred_addr; - -typedef struct ngtcp2_transport_params { - ngtcp2_preferred_addr preferred_address; - /* original_dcid is the Destination Connection ID field from the - first Initial packet from client. Server must specify this - field. It is expected that application knows the original - Destination Connection ID even if it sends Retry packet, for - example, by including it in retry token. Otherwise, application - should not specify this field. */ - ngtcp2_cid original_dcid; - /* initial_scid is the Source Connection ID field from the first - Initial packet the endpoint sends. Application should not - specify this field. */ - ngtcp2_cid initial_scid; - /* retry_scid is the Source Connection ID field from Retry packet. - Only server uses this field. If server application received - Initial packet with retry token from client and server verified - its token, server application must set Destination Connection ID - field from the Initial packet to this field and set - retry_scid_present to nonzero. Server application must verify - that the Destination Connection ID from Initial packet was sent - in Retry packet by, for example, including the Connection ID in a - token, or including it in AAD when encrypting a token. */ - ngtcp2_cid retry_scid; - /* initial_max_stream_data_bidi_local is the size of flow control - window of locally initiated stream. This is the number of bytes - that the remote endpoint can send and the local endpoint must - ensure that it has enough buffer to receive them. */ - uint64_t initial_max_stream_data_bidi_local; - /* initial_max_stream_data_bidi_remote is the size of flow control - window of remotely initiated stream. This is the number of bytes - that the remote endpoint can send and the local endpoint must - ensure that it has enough buffer to receive them. */ - uint64_t initial_max_stream_data_bidi_remote; - /* initial_max_stream_data_uni is the size of flow control window of - remotely initiated unidirectional stream. This is the number of - bytes that the remote endpoint can send and the local endpoint - must ensure that it has enough buffer to receive them. */ - uint64_t initial_max_stream_data_uni; - /* initial_max_data is the connection level flow control window. */ - uint64_t initial_max_data; - /* initial_max_streams_bidi is the number of concurrent streams that - the remote endpoint can create. */ - uint64_t initial_max_streams_bidi; - /* initial_max_streams_uni is the number of concurrent - unidirectional streams that the remote endpoint can create. */ - uint64_t initial_max_streams_uni; - /* max_idle_timeout is a duration during which sender allows - quiescent. */ - ngtcp2_duration max_idle_timeout; - uint64_t max_udp_payload_size; - /* active_connection_id_limit is the maximum number of Connection ID - that sender can store. */ - uint64_t active_connection_id_limit; - uint64_t ack_delay_exponent; - ngtcp2_duration max_ack_delay; - uint8_t stateless_reset_token_present; - uint8_t disable_active_migration; - uint8_t retry_scid_present; - uint8_t preferred_address_present; - uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; -} ngtcp2_transport_params; - -typedef struct ngtcp2_log ngtcp2_log; - -typedef enum ngtcp2_pktns_id { - /* NGTCP2_PKTNS_ID_INITIAL is the Initial packet number space. */ - NGTCP2_PKTNS_ID_INITIAL, - /* NGTCP2_PKTNS_ID_INITIAL is the Handshake packet number space. */ - NGTCP2_PKTNS_ID_HANDSHAKE, - /* NGTCP2_PKTNS_ID_INITIAL is the Application data packet number - space. */ - NGTCP2_PKTNS_ID_APP, - /* NGTCP2_PKTNS_ID_MAX is defined to get the number of packet number - spaces. */ - NGTCP2_PKTNS_ID_MAX -} ngtcp2_pktns_id; - -/** - * @struct - * - * ngtcp2_conn_stat holds various connection statistics, and computed - * data for recovery and congestion controller. - */ -typedef struct ngtcp2_conn_stat { - ngtcp2_duration latest_rtt; - ngtcp2_duration min_rtt; - ngtcp2_duration smoothed_rtt; - ngtcp2_duration rttvar; - ngtcp2_duration initial_rtt; - size_t pto_count; - ngtcp2_tstamp loss_detection_timer; - /* last_tx_pkt_ts corresponds to - time_of_last_sent_ack_eliciting_packet in - draft-ietf-quic-recovery-25. */ - ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX]; - ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX]; - uint64_t cwnd; - uint64_t ssthresh; - ngtcp2_tstamp congestion_recovery_start_ts; - uint64_t bytes_in_flight; - /* max_udp_payload_size is the maximum size of UDP datagram payload - that this endpoint transmits. It is used by congestion - controller to compute congestion window. */ - size_t max_udp_payload_size; - /* bytes_sent is the number of bytes sent in this particular - connection. It only includes data written by - `ngtcp2_conn_writev_stream()` .*/ - uint64_t bytes_sent; - /* bytes_recv is the number of bytes received in this particular - connection, including discarded packets. */ - uint64_t bytes_recv; - /* delivery_rate_sec is the current sending rate measured per - second. */ - uint64_t delivery_rate_sec; - /* recv_rate_sec is the current receiving rate of application data - measured in per second. */ - uint64_t recv_rate_sec; -} ngtcp2_conn_stat; - -typedef enum ngtcp2_cc_algo { - NGTCP2_CC_ALGO_RENO = 0x00, - NGTCP2_CC_ALGO_CUBIC = 0x01, - NGTCP2_CC_ALGO_CUSTOM = 0xff -} ngtcp2_cc_algo; - -typedef struct ngtcp2_cc_base { - ngtcp2_log *log; -} ngtcp2_cc_base; - -/* ngtcp2_cc_pkt is a convenient structure to include acked/lost/sent - packet. */ -typedef struct { - /* pkt_num is the packet number */ - int64_t pkt_num; - /* pktlen is the length of packet. */ - size_t pktlen; - /* pktns_id is the ID of packet number space which this packet - belongs to. */ - ngtcp2_pktns_id pktns_id; - /* ts_sent is the timestamp when packet is sent. */ - ngtcp2_tstamp ts_sent; -} ngtcp2_cc_pkt; - -typedef struct ngtcp2_cc ngtcp2_cc; - -typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt, - ngtcp2_tstamp ts); - -typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, - ngtcp2_tstamp ts); - -typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt); - -typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc); - -typedef enum ngtcp2_cc_event_type { - /* NGTCP2_CC_EVENT_TX_START occurs when ack-eliciting packet is sent - and no other ack-eliciting packet is present. */ - NGTCP2_CC_EVENT_TYPE_TX_START -} ngtcp2_cc_event_type; - -typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_cc_event_type event, ngtcp2_tstamp ts); - -typedef struct ngtcp2_cc { - ngtcp2_cc_base *ccb; - ngtcp2_cc_on_pkt_acked on_pkt_acked; - ngtcp2_cc_congestion_event congestion_event; - ngtcp2_cc_on_persistent_congestion on_persistent_congestion; - ngtcp2_cc_on_ack_recv on_ack_recv; - ngtcp2_cc_on_pkt_sent on_pkt_sent; - ngtcp2_cc_new_rtt_sample new_rtt_sample; - ngtcp2_cc_reset reset; - ngtcp2_cc_event event; -} ngtcp2_cc; - -/* user_data is the same object passed to ngtcp2_conn_client_new or - ngtcp2_conn_server_new. */ -typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...); - -/** - * @enum - * - * :type:`ngtcp2_qlog_write_flag` defines the set of flags passed to - * :type:`ngtcp2_qlog_write` callback. - */ -typedef enum ngtcp2_qlog_write_flag { - NGTCP2_QLOG_WRITE_FLAG_NONE = 0, - /** - * NGTCP2_QLOG_WRITE_FLAG_FIN indicates that this is the final call - * to :type:`ngtcp2_qlog_write` in the current connection. - */ - NGTCP2_QLOG_WRITE_FLAG_FIN = 0x01 -} ngtcp2_qlog_write_flag; - -/** - * @functypedef - * - * :type:`ngtcp2_qlog_write` is a callback function which is called to - * write qlog |data| of length |datalen| bytes. |flags| is bitwise OR - * of zero or more of :type:`ngtcp2_qlog_write_flag`. - */ -typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags, - const void *data, size_t datalen); - -typedef struct ngtcp2_qlog_settings { - /* odcid is Original Destination Connection ID sent by client. It - is used as group_id and ODCID fields. Client ignores this field - and uses dcid parameter passed to `ngtcp2_conn_client_new()`. */ - ngtcp2_cid odcid; - /* write is a callback function to write qlog. Setting NULL - disables qlog. */ - ngtcp2_qlog_write write; -} ngtcp2_qlog_settings; - -typedef struct ngtcp2_settings { - /* transport_params is the QUIC transport parameters to send. */ - ngtcp2_transport_params transport_params; - ngtcp2_qlog_settings qlog; - ngtcp2_cc_algo cc_algo; - ngtcp2_cc *cc; - /* initial_ts is an initial timestamp given to the library. */ - ngtcp2_tstamp initial_ts; - /* initial_rtt is an initial RTT. */ - ngtcp2_duration initial_rtt; - /* log_printf is a function that the library uses to write logs. - NULL means no logging output. */ - ngtcp2_printf log_printf; - /* max_udp_payload_size is the maximum size of UDP datagram payload - that this endpoint transmits. It is used by congestion - controller to compute congestion window. If it is set to 0, it - defaults to NGTCP2_DEFAULT_MAX_PKTLEN. */ - size_t max_udp_payload_size; - /** - * token is a token from Retry packet or NEW_TOKEN frame. - * - * Server sets this field if it received the token in Client Initial - * packet and successfully validated. - * - * Client sets this field if it intends to send token in its Initial - * packet. - * - * `ngtcp2_conn_server_new` and `ngtcp2_conn_client_new` make a copy - * of token. - */ - ngtcp2_vec token; -} ngtcp2_settings; - -/** - * @struct - * - * ngtcp2_addr is the endpoint address. - */ -typedef struct ngtcp2_addr { - /* addrlen is the length of addr. */ - size_t addrlen; - /* addr points to the buffer which contains endpoint address. It - must not be NULL. */ - struct sockaddr *addr; - /* user_data is an arbitrary data and opaque to the library. */ - void *user_data; -} ngtcp2_addr; - -/** - * @struct - * - * ngtcp2_path is the network endpoints where a packet is sent and - * received. - */ -typedef struct ngtcp2_path { - /* local is the address of local endpoint. */ - ngtcp2_addr local; - /* remote is the address of remote endpoint. */ - ngtcp2_addr remote; -} ngtcp2_path; - -/** - * @struct - * - * ngtcp2_path_storage is a convenient struct to have buffers to store - * the longest addresses. - */ -typedef struct ngtcp2_path_storage { - struct sockaddr_storage local_addrbuf; - struct sockaddr_storage remote_addrbuf; - ngtcp2_path path; -} ngtcp2_path_storage; - -/** - * @struct - * - * `ngtcp2_crypto_md` is a wrapper around native message digest - * object. - * - * If libngtcp2_crypto_openssl is linked, native_handle must be a - * pointer to EVP_MD. - */ -typedef struct ngtcp2_crypto_md { - void *native_handle; -} ngtcp2_crypto_md; - -/** - * @struct - * - * `ngtcp2_crypto_aead` is a wrapper around native AEAD object. - * - * If libngtcp2_crypto_openssl is linked, native_handle must be a - * pointer to EVP_CIPHER. - */ -typedef struct ngtcp2_crypto_aead { - void *native_handle; -} ngtcp2_crypto_aead; - -/** - * @struct - * - * `ngtcp2_crypto_cipher` is a wrapper around native cipher object. - * - * If libngtcp2_crypto_openssl is linked, native_handle must be a - * pointer to EVP_CIPHER. - */ -typedef struct ngtcp2_crypto_cipher { - void *native_handle; -} ngtcp2_crypto_cipher; - -/** - * @struct - * - * `ngtcp2_crypto_aead_ctx` is a wrapper around native AEAD cipher - * context object. It should be initialized with a specific key. - * ngtcp2 library reuses this context object to encrypt or decrypt - * multiple packets. - */ -typedef struct ngtcp2_crypto_aead_ctx { - void *native_handle; -} ngtcp2_crypto_aead_ctx; - -/** - * @struct - * - * `ngtcp2_crypto_cipher_ctx` is a wrapper around native cipher - * context object. It should be initialized with a specific key. - * ngtcp2 library reuses this context object to encrypt or decrypt - * multiple packet headers. - */ -typedef struct ngtcp2_crypto_cipher_ctx { - void *native_handle; -} ngtcp2_crypto_cipher_ctx; - -/** - * @function - * - * `ngtcp2_crypto_ctx` is a convenient structure to bind all crypto - * related objects in one place. Use `ngtcp2_crypto_ctx_initial` to - * initialize this struct for Initial packet encryption. For - * Handshake and Shortpackets, use `ngtcp2_crypto_ctx_tls`. - */ -typedef struct ngtcp2_crypto_ctx { - ngtcp2_crypto_aead aead; - ngtcp2_crypto_md md; - ngtcp2_crypto_cipher hp; - /* max_encryption is the number of encryption which this key can be - used with. */ - uint64_t max_encryption; - /* max_decryption_failure is the number of decryption failure with - this key. */ - uint64_t max_decryption_failure; -} ngtcp2_crypto_ctx; - -/** - * @function - * - * `ngtcp2_encode_transport_params` encodes |params| in |dest| of - * length |destlen|. - * - * This function returns the number of written, or one of the - * following negative error codes: - * - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small. - * :enum:`NGTCP2_ERR_INVALID_ARGUMENT`: - * |exttype| is invalid. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( - uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype, - const ngtcp2_transport_params *params); - -/** - * @function - * - * `ngtcp2_decode_transport_params` decodes transport parameters in - * |data| of length |datalen|, and stores the result in the object - * pointed by |params|. - * - * If the optional parameters are missing, the default value is - * assigned. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` - * The required parameter is missing. - * :enum:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` - * The input is malformed. - * :enum:`NGTCP2_ERR_INVALID_ARGUMENT`: - * |exttype| is invalid. - */ -NGTCP2_EXTERN int -ngtcp2_decode_transport_params(ngtcp2_transport_params *params, - ngtcp2_transport_params_type exttype, - const uint8_t *data, size_t datalen); - -/** - * @function - * - * `ngtcp2_pkt_decode_version_cid` extracts QUIC version, Destination - * Connection ID and Source Connection ID from the packet pointed by - * |data| of length |datalen|. This function can handle Connection ID - * up to 255 bytes unlike `ngtcp2_pkt_decode_hd_long` or - * `ngtcp2_pkt_decode_hd_short` which are only capable of handling - * Connection ID less than or equal to :macro:`NGTCP2_MAX_CIDLEN`. - * Longer Connection ID is only valid if the version is unsupported - * QUIC version. - * - * If the given packet is Long packet, this function extracts the - * version from the packet and assigns it to |*pversion|. It also - * extracts the pointer to the Destination Connection ID and its - * length and assigns them to |*pdcid| and |*pdcidlen| respectively. - * Similarly, it extracts the pointer to the Source Connection ID and - * its length and assigns them to |*pscid| and |*pscidlen| - * respectively. - * - * If the given packet is Short packet, |*pversion| will be - * :macro:`NGTCP2_PROTO_VER`, |*pscid| will be NULL, and |*pscidlen| - * will be 0. Because the Short packet does not have the length of - * Destination Connection ID, the caller has to pass the length in - * |short_dcidlen|. This function extracts the pointer to the - * Destination Connection ID and assigns it to |*pdcid|. - * |short_dcidlen| is assigned to |*pdcidlen|. - * - * This function returns 0 or 1 if it succeeds. It returns 1 if - * Version Negotiation packet should be sent. Otherwise, one of the - * following negative error code: - * - * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` - * The function could not decode the packet header. - */ -NGTCP2_EXTERN int -ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, - size_t *pdcidlen, const uint8_t **pscid, - size_t *pscidlen, const uint8_t *data, - size_t datalen, size_t short_dcidlen); - -/** - * @function - * - * `ngtcp2_pkt_decode_hd_long` decodes QUIC long packet header in - * |pkt| of length |pktlen|. This function only parses the input just - * before packet number field. - * - * This function does not verify that length field is correct. In - * other words, this function succeeds even if length > |pktlen|. - * - * This function can handle Connection ID up to - * :enum:`NGTCP2_MAX_CIDLEN`. Consider to use - * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. - * - * This function handles Version Negotiation specially. If version - * field is 0, |pkt| must contain Version Negotiation packet. Version - * Negotiation packet has random type in wire format. For - * convenience, this function sets - * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` to dest->type, and set - * dest->payloadlen and dest->pkt_num to 0. Version Negotiation - * packet occupies a single packet. - * - * It stores the result in the object pointed by |dest|, and returns - * the number of bytes decoded to read the packet header if it - * succeeds, or one of the following error codes: - * - * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` - * Packet is too short; or it is not a long header - * :enum:`NGTCP2_ERR_UNKNOWN_PKT_TYPE` - * Packet type is unknown - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, - const uint8_t *pkt, - size_t pktlen); - -/** - * @function - * - * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in - * |pkt| of length |pktlen|. |dcidlen| is the length of DCID in - * packet header. Short packet does not encode the length of - * connection ID, thus we need the input from the outside. This - * function only parses the input just before packet number field. - * This function can handle Connection ID up to - * :enum:`NGTCP2_MAX_CIDLEN`. Consider to use - * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. It - * stores the result in the object pointed by |dest|, and returns the - * number of bytes decoded to read the packet header if it succeeds, - * or one of the following error codes: - * - * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` - * Packet is too short; or it is not a short header - * :enum:`NGTCP2_ERR_UNKNOWN_PKT_TYPE` - * Packet type is unknown - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, - const uint8_t *pkt, - size_t pktlen, - size_t dcidlen); - -/** - * @function - * - * `ngtcp2_pkt_write_stateless_reset` writes Stateless Reset packet in - * the buffer pointed by |dest| whose length is |destlen|. - * |stateless_reset_token| is a pointer to the Stateless Reset Token, - * and its length must be :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` - * bytes long. |rand| specifies the random octets preceding Stateless - * Reset Token. The length of |rand| is specified by |randlen| which - * must be at least :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN` bytes - * long. - * - * If |randlen| is too long to write them all in the buffer, |rand| is - * written to the buffer as much as possible, and is truncated. - * - * This function returns the number of bytes written to the buffer, or - * one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small. - * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` - * |randlen| is strictly less than - * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset( - uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token, - const uint8_t *rand, size_t randlen); - -/** - * @function - * - * `ngtcp2_pkt_write_version_negotiation` writes Version Negotiation - * packet in the buffer pointed by |dest| whose length is |destlen|. - * |unused_random| should be generated randomly. |dcid| is the - * destination connection ID which appears in a packet as a source - * connection ID sent by client which caused version negotiation. - * Similarly, |scid| is the source connection ID which appears in a - * packet as a destination connection ID sent by client. |sv| is a - * list of supported versions, and |nsv| specifies the number of - * supported versions included in |sv|. - * - * This function returns the number of bytes written to the buffer, or - * one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_version_negotiation( - uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid, - size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv, - size_t nsv); - -/** - * @function - * - * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is - * the first byte of Long packet header. - */ -NGTCP2_EXTERN uint8_t ngtcp2_pkt_get_type_long(uint8_t c); - -struct ngtcp2_conn; - -typedef struct ngtcp2_conn ngtcp2_conn; - -/** - * @functypedef - * - * :type:`ngtcp2_client_initial` is invoked when client application - * asks TLS stack to produce first TLS cryptographic handshake data. - * - * This implementation of this callback must get the first handshake - * data from TLS stack and pass it to ngtcp2 library using - * `ngtcp2_conn_submit_crypto_data` function. Make sure that before - * calling `ngtcp2_conn_submit_crypto_data` function, client - * application must create initial packet protection keys and IVs, and - * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key` - * and - * - * This callback function must return 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call - * return immediately. - * - * TODO: Define error code for TLS stack failure. Suggestion: - * NGTCP2_ERR_CRYPTO. - */ -typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_recv_client_initial` is invoked when server receives - * Initial packet from client. An server application must implement - * this callback, and generate initial keys and IVs for both - * transmission and reception. Install them using - * `ngtcp2_conn_set_initial_key`. |dcid| is the destination - * connection ID which client generated randomly. It is used to - * derive initial packet protection keys. - * - * The callback function must return 0 if it succeeds. If an error - * occurs, return :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the - * library call return immediately. - * - * TODO: Define error code for TLS stack failure. Suggestion: - * NGTCP2_ERR_CRYPTO. - */ -typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn, - const ngtcp2_cid *dcid, - void *user_data); - -/** - * @enum - * - * ngtcp2_crypto_level is encryption level. - */ -#if defined(__cplusplus) && __cplusplus >= 201103L -typedef enum ngtcp2_crypto_level : int { -#else -typedef enum ngtcp2_crypto_level { -#endif - /** - * NGTCP2_CRYPTO_LEVEL_INITIAL is Initial Keys encryption level. - */ - NGTCP2_CRYPTO_LEVEL_INITIAL, - /** - * NGTCP2_CRYPTO_LEVEL_HANDSHAKE is Handshake Keys encryption level. - */ - NGTCP2_CRYPTO_LEVEL_HANDSHAKE, - /** - * NGTCP2_CRYPTO_LEVEL_APP is Application Data (1-RTT) Keys - * encryption level. - */ - NGTCP2_CRYPTO_LEVEL_APP, - /** - * NGTCP2_CRYPTO_LEVEL_EARLY is Early Data (0-RTT) Keys encryption - * level. - */ - NGTCP2_CRYPTO_LEVEL_EARLY -} ngtcp2_crypto_level; - -/** - * @functypedef - * - * :type`ngtcp2_recv_crypto_data` is invoked when crypto data is - * received. The received data is pointed to by |data|, and its length - * is |datalen|. The |offset| specifies the offset where |data| is - * positioned. |user_data| is the arbitrary pointer passed to - * `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. The ngtcp2 - * library ensures that the crypto data is passed to the application - * in the increasing order of |offset|. |datalen| is always strictly - * greater than 0. |crypto_level| indicates the encryption level - * where this data is received. Crypto data can never be received in - * :enum:`NGTCP2_CRYPTO_LEVEL_EARLY`. - * - * The application should provide the given data to TLS stack. - * - * The callback function must return 0 if it succeeds. If TLS stack - * reported error, return :enum:`NGTCP2_ERR_CRYPTO`. If application - * encounters fatal error, return :enum:`NGTCP2_ERR_CALLBACK_FAILURE` - * which makes the library call return immediately. If the other - * value is returned, it is treated as - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. - */ -typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, const uint8_t *data, - size_t datalen, void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_handshake_completed` is invoked when QUIC - * cryptographic handshake has completed. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_handshake_completed)(ngtcp2_conn *conn, void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_handshake_confirmed` is invoked when QUIC - * cryptographic handshake is confirmed. The handshake confirmation - * means that both endpoints agree that handshake has finished. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_handshake_confirmed)(ngtcp2_conn *conn, void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_recv_version_negotiation` is invoked when Version - * Negotiation packet is received. |hd| is the pointer to the QUIC - * packet header object. The vector |sv| of |nsv| elements contains - * the QUIC version the server supports. Since Version Negotiation is - * only sent by server, this callback function is used by client only. - * - * The callback function must return 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call - * return immediately. - */ -typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn, - const ngtcp2_pkt_hd *hd, - const uint32_t *sv, size_t nsv, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received. - * This callback is client only. - * - * Application must regenerate packet protection key, IV, and header - * protection key for Initial packets using the destination connection - * ID obtained by `ngtcp2_conn_get_dcid()` and install them by calling - * `ngtcp2_conn_install_initial_key()`. - * - * 0-RTT data accepted by the ngtcp2 library will be retransmitted by - * the library automatically. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_encrypt` is invoked when the ngtcp2 library asks the - * application to encrypt packet payload. The packet payload to - * encrypt is passed as |plaintext| of length |plaintextlen|. The - * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context - * object which is initialized with encryption key. The nonce is - * passed as |nonce| of length |noncelen|. The ad, Additional Data to - * AEAD, is passed as |ad| of length |adlen|. - * - * The implementation of this callback must encrypt |plaintext| using - * the negotiated cipher suite and write the ciphertext into the - * buffer pointed by |dest|. |dest| has enough capacity to store the - * ciphertext. - * - * |dest| and |plaintext| may point to the same buffer. - * - * The callback function must return 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call - * return immediately. - */ -typedef int (*ngtcp2_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 *ad, size_t adlen); - -/** - * @functypedef - * - * :type:`ngtcp2_decrypt` is invoked when the ngtcp2 library asks the - * application to decrypt packet payload. The packet payload to - * decrypt is passed as |ciphertext| of length |ciphertextlen|. The - * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context - * object which is initialized with decryption key. The nonce is - * passed as |nonce| of length |noncelen|. The ad, Additional Data to - * AEAD, is passed as |ad| of length |adlen|. - * - * The implementation of this callback must decrypt |ciphertext| using - * the negotiated cipher suite and write the ciphertext into the - * buffer pointed by |dest|. |dest| has enough capacity to store the - * cleartext. - * - * |dest| and |ciphertext| may point to the same buffer. - * - * The callback function must return 0 if it succeeds. If TLS stack - * fails to decrypt data, return :enum:`NGTCP2_ERR_TLS_DECRYPT`. For - * any other errors, return :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which - * makes the library call return immediately. - */ -typedef int (*ngtcp2_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 *ad, size_t adlen); - -/** - * @functypedef - * - * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the - * application to produce mask to encrypt or decrypt packet header. - * The encryption cipher is |hp|. |hp_ctx| is the cipher context - * object which is initialized with header protection key. The sample - * is passed as |sample|. - * - * The implementation of this callback must produce a mask using the - * header protection cipher suite specified by QUIC specification and - * write the result into the buffer pointed by |dest|. The length of - * mask must be :macro:`NGTCP2_HP_MASKLEN`. The library ensures that - * |dest| has enough capacity. - * - * The callback function must return 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call - * return immediately. - */ -typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - const uint8_t *sample); - -/** - * @enum - * - * ngtcp2_stream_data_flag defines the properties of the data emitted - * via :type:`ngtcp2_recv_stream_data` callback function. - */ -typedef enum ngtcp2_stream_data_flag { - NGTCP2_STREAM_DATA_FLAG_NONE = 0x00, - /** - * NGTCP2_STREAM_DATA_FLAG_FIN indicates that this chunk of data is - * final piece of an incoming stream. - */ - NGTCP2_STREAM_DATA_FLAG_FIN = 0x01, - /** - * NGTCP2_STREAM_DATA_FLAG_0RTT indicates that this chunk of data - * contains data received in 0RTT packet and the handshake has not - * completed yet, which means that the data might be replayed. - */ - NGTCP2_STREAM_DATA_FLAG_0RTT = 0x02 -} ngtcp2_stream_data_flag; - -/** - * @functypedef - * - * :type:`ngtcp2_recv_stream_data` is invoked when stream data is - * received. The stream is specified by |stream_id|. |flags| is the - * bitwise-OR of zero or more of ngtcp2_stream_data_flag. If |flags| - * & :enum:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of - * the data is the last data in this stream. |offset| is the offset - * where this data begins. The library ensures that data is passed to - * the application in the non-decreasing order of |offset|. The data - * is passed as |data| of length |datalen|. |datalen| may be 0 if and - * only if |fin| is nonzero. - * - * The callback function must return 0 if it succeeds, or - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return - * immediately. - */ -typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint32_t flags, - int64_t stream_id, uint64_t offset, - const uint8_t *data, size_t datalen, - void *user_data, void *stream_user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_stream_open` is a callback function which is called - * when remote stream is opened by peer. This function is not called - * if stream is opened by implicitly (we might reconsider this - * behaviour). - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_stream_close` is invoked when a stream is closed. - * This callback is not called when QUIC connection is closed before - * existing streams are closed. |app_error_code| indicates the error - * code of this closure. - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_stream_reset` is invoked when a stream identified by - * |stream_id| is reset by a remote endpoint. - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id, - uint64_t final_size, uint64_t app_error_code, - void *user_data, void *stream_user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_acked_stream_data_offset` is a callback function - * which is called when stream data is acked, and application can free - * the data. The acked range of data is [offset, offset + datalen). - * For a given stream_id, this callback is called sequentially in - * increasing order of |offset|. |datalen| is normally strictly - * greater than 0. One exception is that when a packet which includes - * STREAM frame which has fin flag set, and 0 length data, this - * callback is invoked with 0 passed as |datalen|. - * - * If a stream is closed prematurely and stream data is still - * in-flight, this callback function is not called for those data. - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_acked_stream_data_offset)( - ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen, - void *user_data, void *stream_user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_acked_crypto_offset` is a callback function which is - * called when crypto stream data is acknowledged, and application can - * free the data. |crypto_level| indicates the encryption level where - * this data was sent. Crypto data never be sent in - * :enum:`NGTCP2_CRYPTO_LEVEL_EARLY`. This works like - * :type:`ngtcp2_acked_stream_data_offset` but crypto stream has no - * stream_id and stream_user_data, and |datalen| never become 0. - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_acked_crypto_offset)(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, uint64_t datalen, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_recv_stateless_reset` is a callback function which is - * called when Stateless Reset packet is received. The stateless - * reset details are given in |sr|. - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_recv_stateless_reset)(ngtcp2_conn *conn, - const ngtcp2_pkt_stateless_reset *sr, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_extend_max_streams` is a callback function which is - * called every time max stream ID is strictly extended. - * |max_streams| is the cumulative number of streams which an endpoint - * can open. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_extend_max_streams)(ngtcp2_conn *conn, - uint64_t max_streams, void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_extend_max_stream_data` is a callback function which - * is invoked when max stream data is extended. |stream_id| - * identifies the stream. |max_data| is a cumulative number of bytes - * the endpoint can send on this stream. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn, - int64_t stream_id, - uint64_t max_data, void *user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_rand` is a callback function to get randomized byte - * string from application. Application must fill random |destlen| - * bytes to the buffer pointed by |dest|. |ctx| provides the context - * how the provided random byte string is used. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_rand)(uint8_t *dest, size_t destlen, ngtcp2_rand_ctx ctx); - -/** - * @functypedef - * - * :type:`ngtcp2_get_new_connection_id` is a callback function to ask - * an application for new connection ID. Application must generate - * new unused connection ID with the exact |cidlen| bytes and store it - * in |cid|. It also has to generate stateless reset token into - * |token|. The length of stateless reset token is - * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` and it is guaranteed that - * the buffer pointed by |cid| has the sufficient space to store the - * token. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_get_new_connection_id)(ngtcp2_conn *conn, ngtcp2_cid *cid, - uint8_t *token, size_t cidlen, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_remove_connection_id` is a callback function which - * notifies the application that connection ID |cid| is no longer used - * by remote endpoint. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_remove_connection_id)(ngtcp2_conn *conn, - const ngtcp2_cid *cid, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_update_key` is a callback function which tells the - * application that it must generate new packet protection keying - * materials and AEAD cipher context objects with new keys. The - * current set of secrets are given as |current_rx_secret| and - * |current_tx_secret| of length |secretlen|. They are decryption and - * encryption secrets respectively. - * - * The application has to generate new secrets and keys for both - * encryption and decryption, and write decryption secret and IV to - * the buffer pointed by |rx_secret| and |rx_iv| respectively. It - * also has to create new AEAD cipher context object with new - * decryption key and initialize |rx_aead_ctx| with it. Similarly, - * write encryption secret and IV to the buffer pointed by |tx_secret| - * and |tx_iv|. Create new AEAD cipher context object with new - * encryption key and initialize |tx_aead_ctx| with it. All given - * buffers have the enough capacity to store secret, key and IV. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_update_key)( - ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, - ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, - ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, - const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, - size_t secretlen, void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_path_validation` is a callback function which tells - * the application the outcome of path validation. |path| is the path - * that was validated. If |res| is - * :enum:`NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, the path validation - * succeeded. If |res| is - * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, the path validation - * failed. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, - const ngtcp2_path *path, - ngtcp2_path_validation_result res, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_select_preferred_addr` is a callback function which - * asks a client application to choose server address from preferred - * addresses |paddr| received from server. An application should - * write preferred address in |dest|. If an application denies the - * preferred addresses, just leave |dest| unmodified (or set dest->len - * to 0) and return 0. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn, - ngtcp2_addr *dest, - const ngtcp2_preferred_addr *paddr, - void *user_data); - -typedef enum ngtcp2_connection_id_status_type { - /* NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE indicates that a local - endpoint starts using new destination Connection ID. */ - NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, - /* NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE indicates that a - local endpoint stops using a given destination Connection ID. */ - NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE -} ngtcp2_connection_id_status_type; - -/** - * @functypedef - * - * :type:`ngtcp2_connection_id_status` is a callback function which is - * called when the status of Connection ID changes. - * - * |token| is the associated stateless reset token and it is NULL if - * no token is present. - * - * |type| is the one of the value defined in - * :enum:`ngtcp2_connection_id_status_type`. The new value might be - * added in the future release. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_connection_id_status)(ngtcp2_conn *conn, int type, - uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_recv_new_token` is a callback function which is - * called when new token is received from server. - * - * |token| is the received token. - * - * The callback function must return 0 if it succeeds. Returning - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. - */ -typedef int (*ngtcp2_recv_new_token)(ngtcp2_conn *conn, const ngtcp2_vec *token, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which - * must delete the native object pointed by |aead_ctx|->native_handle. - */ -typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn, - ngtcp2_crypto_aead_ctx *aead_ctx, - void *user_data); - -/** - * @functypedef - * - * :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function - * which must delete the native object pointed by - * |cipher_ctx|->native_handle. - */ -typedef void (*ngtcp2_delete_crypto_cipher_ctx)( - ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); - -typedef struct ngtcp2_conn_callbacks { - /** - * client_initial is a callback function which is invoked when - * client asks TLS stack to produce first TLS cryptographic - * handshake message. This callback function must be specified. - */ - ngtcp2_client_initial client_initial; - /** - * recv_client_initial is a callback function which is invoked when - * a server receives the first packet from client. This callback - * function must be specified. - */ - ngtcp2_recv_client_initial recv_client_initial; - /** - * recv_crypto_data is a callback function which is invoked when - * cryptographic data (CRYPTO frame, in other words, TLS message) is - * received. This callback function must be specified. - */ - ngtcp2_recv_crypto_data recv_crypto_data; - /** - * handshake_completed is a callback function which is invoked when - * QUIC cryptographic handshake has completed. This callback - * function is optional. - */ - ngtcp2_handshake_completed handshake_completed; - /** - * recv_version_negotiation is a callback function which is invoked - * when Version Negotiation packet is received by a client. This - * callback function is optional. - */ - ngtcp2_recv_version_negotiation recv_version_negotiation; - /** - * encrypt is a callback function which is invoked to encrypt a QUIC - * packet. This callback function must be specified. - */ - ngtcp2_encrypt encrypt; - /** - * decrypt is a callback function which is invoked to decrypt a QUIC - * packet. This callback function must be specified. - */ - ngtcp2_decrypt decrypt; - /** - * hp_mask is a callback function which is invoked to get a mask to - * encrypt or decrypt packet header. This callback function must be - * specified. - */ - ngtcp2_hp_mask hp_mask; - /** - * recv_stream_data is a callback function which is invoked when - * STREAM data, which includes application data, is received. This - * callback function is optional. - */ - ngtcp2_recv_stream_data recv_stream_data; - /** - * acked_crypto_offset is a callback function which is invoked when - * CRYPTO data is acknowledged by a remote endpoint. It tells an - * application the largest offset of acknowledged CRYPTO data - * without a gap so that the application can free memory for the - * data. This callback function is optional. - */ - ngtcp2_acked_crypto_offset acked_crypto_offset; - /** - * acked_stream_data_offset is a callback function which is invoked - * when STREAM data, which includes application data, is - * acknowledged by a remote endpoint. It tells an application the - * largest offset of acknowledged STREAM data without a gap so that - * application can free memory for the data. This callback function - * is optional. - */ - ngtcp2_acked_stream_data_offset acked_stream_data_offset; - /** - * stream_open is a callback function which is invoked when new - * remote stream is opened by a remote endpoint. This callback - * function is optional. - */ - ngtcp2_stream_open stream_open; - /** - * stream_close is a callback function which is invoked when a - * stream is closed. This callback function is optional. - */ - ngtcp2_stream_close stream_close; - /** - * recv_stateless_reset is a callback function which is invoked when - * Stateless Reset packet is received. This callback function is - * optional. - */ - ngtcp2_recv_stateless_reset recv_stateless_reset; - /** - * recv_retry is a callback function which is invoked when a client - * receives Retry packet. For client, this callback function must - * be specified. Server never receive Retry packet. - */ - ngtcp2_recv_retry recv_retry; - /** - * extend_max_local_streams_bidi is a callback function which is - * invoked when the number of bidirectional stream which a local - * endpoint can open is increased. This callback function is - * optional. - */ - ngtcp2_extend_max_streams extend_max_local_streams_bidi; - /** - * extend_max_local_streams_uni is a callback function which is - * invoked when the number of unidirectional stream which a local - * endpoint can open is increased. This callback function is - * optional. - */ - ngtcp2_extend_max_streams extend_max_local_streams_uni; - /** - * rand is a callback function which is invoked when the library - * needs unpredictable sequence of random data. This callback - * function must be specified. - */ - ngtcp2_rand rand; - /** - * get_new_connection_id is a callback function which is invoked - * when the library needs new connection ID. This callback function - * must be specified. - */ - ngtcp2_get_new_connection_id get_new_connection_id; - /** - * remove_connection_id is a callback function which notifies an - * application that connection ID is no longer used by a remote - * endpoint. This callback function is optional. - */ - ngtcp2_remove_connection_id remove_connection_id; - /** - * update_key is a callback function which is invoked when the - * library tells an application that it must update keying materials - * and install new keys. This function must be specified. - */ - ngtcp2_update_key update_key; - /** - * path_validation is a callback function which is invoked when path - * validation completed. This function is optional. - */ - ngtcp2_path_validation path_validation; - /** - * select_preferred_addr is a callback function which is invoked - * when the library asks a client to select preferred address - * presented by a server. This function is optional. - */ - ngtcp2_select_preferred_addr select_preferred_addr; - /** - * stream_reset is a callback function which is invoked when a - * stream is reset by a remote endpoint. This callback function is - * optional. - */ - ngtcp2_stream_reset stream_reset; - /** - * extend_max_remote_streams_bidi is a callback function which is - * invoked when the number of bidirectional streams which a remote - * endpoint can open is increased. This callback function is - * optional. - */ - ngtcp2_extend_max_streams extend_max_remote_streams_bidi; - /** - * extend_max_remote_streams_uni is a callback function which is - * invoked when the number of unidirectional streams which a remote - * endpoint can open is increased. This callback function is - * optional. - */ - ngtcp2_extend_max_streams extend_max_remote_streams_uni; - /** - * extend_max_stream_data is callback function which is invoked when - * the maximum offset of STREAM data that a local endpoint can send - * is increased. This callback function is optional. - */ - ngtcp2_extend_max_stream_data extend_max_stream_data; - /** - * dcid_status is a callback function which is invoked when the new - * destination Connection ID is activated or the activated - * destination Connection ID is now deactivated. - */ - ngtcp2_connection_id_status dcid_status; - /** - * handshake_confirmed is a callback function which is invoked when - * both endpoints agree that handshake has finished. This field is - * ignored by server because handshake_completed indicates the - * handshake confirmation for server. - */ - ngtcp2_handshake_confirmed handshake_confirmed; - /** - * recv_new_token is a callback function which is invoked when new - * token is received from server. This field is ignored by server. - */ - ngtcp2_recv_new_token recv_new_token; - /** - * delete_crypto_aead_ctx is a callback function which deletes a - * given AEAD cipher context object. - */ - ngtcp2_delete_crypto_aead_ctx delete_crypto_aead_ctx; - /** - * delete_crypto_cipher_ctx is a callback function which deletes a - * given cipher context object. - */ - ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx; -} ngtcp2_conn_callbacks; - -/** - * @function - * - * `ngtcp2_pkt_write_connection_close` writes Initial packet - * containing CONNECTION_CLOSE frame with the given |error_code| to - * the buffer pointed by |dest| of length |destlen|. All encryption - * parameters are for Initial packet encryption. The packet number is - * always 0. - * - * The primary use case of this function is for server to send - * CONNECTION_CLOSE frame in Initial packet to close connection - * without committing the state when validating Retry token fails. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small. - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE - * Callback function failed. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( - uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, - const ngtcp2_crypto_cipher_ctx *hp_ctx); - -/** - * @function - * - * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed - * by |dest| whose length is |destlen|. |odcid| specifies Original - * Destination Connection ID. |token| specifies Retry Token, and - * |tokenlen| specifies its length. |aead| must be AEAD_AES_128_GCM. - * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as an - * encryption key. - * - * This function returns the number of bytes written to the buffer, or - * one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small. - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE - * Callback function failed. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( - uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, - size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_aead_ctx *aead_ctx); - -/** - * @function - * - * `ngtcp2_accept` is used by server implementation, and decides - * whether packet |pkt| of length |pktlen| is acceptable for initial - * packet from client. - * - * If it is acceptable, it returns 0. If it is not acceptable, and - * Version Negotiation packet is required to send, it returns 1. - * Otherwise, it returns -1. - * - * If |dest| is not NULL, and the return value is 0 or 1, the decoded - * packet header is stored to the object pointed by |dest|. - */ -NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, - size_t pktlen); - -/** - * @function - * - * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and - * initializes it as client. |dcid| is randomized destination - * connection ID. |scid| is source connection ID. |version| is a - * QUIC version to use. |path| is the network path where this QUIC - * connection is being established and must not be NULL. |callbacks|, - * and |settings| must not be NULL, and the function make a copy of - * each of them. |user_data| is the arbitrary pointer which is passed - * to the user-defined callback functions. If |mem| is NULL, the - * memory allocator returned by `ngtcp2_mem_default()` is used. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int -ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_conn_callbacks *callbacks, - const ngtcp2_settings *settings, const ngtcp2_mem *mem, - void *user_data); - -/** - * @function - * - * `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and - * initializes it as server. |dcid| is a destination connection ID. - * |scid| is a source connection ID. |path| is the network path where - * this QUIC connection is being established and must not be NULL. - * |version| is a QUIC version to use. |callbacks|, and |settings| - * must not be NULL, and the function make a copy of each of them. - * |user_data| is the arbitrary pointer which is passed to the - * user-defined callback functions. If |mem| is NULL, the memory - * allocator returned by `ngtcp2_mem_default()` is used. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int -ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_conn_callbacks *callbacks, - const ngtcp2_settings *settings, const ngtcp2_mem *mem, - void *user_data); - -/** - * @function - * - * `ngtcp2_conn_del` frees resources allocated for |conn|. It also - * frees memory pointed by |conn|. - */ -NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of - * length |pktlen| and processes it. |path| is the network path the - * packet is delivered and must not be NULL. This function performs - * QUIC handshake as well. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns 0 if it succeeds, or negative error codes. - * In general, if the error code which satisfies - * ngtcp2_erro_is_fatal(err) != 0 is returned, the application should - * just close the connection by calling - * `ngtcp2_conn_write_connection_close` or just delete the QUIC - * connection using `ngtcp2_conn_del`. It is undefined to call the - * other library functions. If :enum:`NGTCP2_ERR_RETRY` is returned, - * application must be a server and it must perform address validation - * by sending Retry packet and close the connection. If - * :enum:`NGTCP2_ERR_DROP_CONN` is returned, server application must - * drop the connection silently (without sending any CONNECTION_CLOSE - * frame) and discard connection state. - */ -NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, - const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_pkt` is equivalent to calling - * `ngtcp2_conn_writev_stream` without specifying stream data and - * :enum:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, - ngtcp2_path *path, - uint8_t *dest, size_t destlen, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC - * handshake has completed. - */ -NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake - * has completed. - */ -NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_install_initial_key` installs packet protection keying - * materials for Initial packets. |rx_aead_ctx| is AEAD cipher - * context object and must be initialized with decryption key, IV - * |rx_iv| of length |rx_ivlen|, and packet header protection cipher - * context object |rx_hp_ctx| to decrypt incoming Initial packets. - * Similarly, |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for - * encrypting outgoing packets and are the same length with the - * decryption counterpart . If they have already been set, they are - * overwritten. - * - * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, - * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. - * :type:`ngtcp2_delete_crypto_aead_ctx` and - * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete - * these objects when they are no longer used. If this function - * fails, the caller is responsible to delete them. - * - * After receiving Retry packet, the DCID most likely changes. In - * that case, client application must generate these keying materials - * again based on new DCID and install them again. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int ngtcp2_conn_install_initial_key( - ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx, - const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, - const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, - const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); - -/** - * @function - * - * `ngtcp2_conn_install_rx_handshake_key` installs packet protection - * keying materials for decrypting incoming Handshake packets. - * |aead_ctx| is AEAD cipher context object which must be initialized - * with decryption key, IV |iv| of length |ivlen|, and packet header - * protection cipher context object |hp_ctx| to decrypt incoming - * Handshake packets. - * - * If this function succeeds, |conn| takes ownership of |aead_ctx|, - * and |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and - * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete - * these objects when they are no longer used. If this function - * fails, the caller is responsible to delete them. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int ngtcp2_conn_install_rx_handshake_key( - ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); - -/** - * @function - * - * `ngtcp2_conn_install_tx_handshake_key` installs packet protection - * keying materials for encrypting outgoing Handshake packets. - * |aead_ctx| is AEAD cipher context object which must be initialized - * with encryption key, IV |iv| of length |ivlen|, and packet header - * protection cipher context object |hp_ctx| to encrypt outgoing - * Handshake packets. - * - * If this function succeeds, |conn| takes ownership of |aead_ctx| and - * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and - * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete - * these objects when they are no longer used. If this function - * fails, the caller is responsible to delete them. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int ngtcp2_conn_install_tx_handshake_key( - ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); - -/** - * @function - * - * `ngtcp2_conn_set_aead_overhead` tells the ngtcp2 library the length - * of AEAD tag which the negotiated cipher suites defines. This - * function must be called before encrypting or decrypting the - * incoming packets other than Initial packets. - */ -NGTCP2_EXTERN void ngtcp2_conn_set_aead_overhead(ngtcp2_conn *conn, - size_t aead_overhead); - -/** - * @function - * - * `ngtcp2_conn_get_aead_overhead` returns the aead overhead passed to - * `ngtcp2_conn_set_aead_overhead`. If `ngtcp2_conn_set_aead_overhead` hasn't - * been called yet this function returns 0. - */ -NGTCP2_EXTERN size_t ngtcp2_conn_get_aead_overhead(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_install_early_key` installs packet protection AEAD - * cipher context object |aead_ctx|, IV |iv| of length |ivlen|, and - * packet header protection cipher context object |hp_ctx| to encrypt - * (for client) or decrypt (for server) 0RTT packets. - * - * If this function succeeds, |conn| takes ownership of |aead_ctx| and - * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and - * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete - * these objects when they are no longer used. If this function - * fails, the caller is responsible to delete them. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int ngtcp2_conn_install_early_key( - ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); - -/** - * @function - * - * `ngtcp2_conn_install_rx_key` installs packet protection keying - * materials for decrypting Short packets. |secret| of length - * |secretlen| is the decryption secret which is used to derive keying - * materials passed to this function. |aead_ctx| is AEAD cipher - * context object which must be initialized with decryption key, IV - * |iv| of length |ivlen|, and packet header protection cipher context - * object |hp_ctx| to decrypt incoming Short packets. - * - * If this function succeeds, |conn| takes ownership of |aead_ctx| and - * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and - * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete - * these objects when they are no longer used. If this function - * fails, the caller is responsible to delete them. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int ngtcp2_conn_install_rx_key( - ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, - const ngtcp2_crypto_cipher_ctx *hp_ctx); - -/** - * @function - * - * `ngtcp2_conn_install_tx_key` installs packet protection keying - * materials for encrypting Short packets. |secret| of length - * |secretlen| is the encryption secret which is used to derive keying - * materials passed to this function. |aead_ctx| is AEAD cipher - * context object which must be initialized with encryption key, IV - * |iv| of length |ivlen|, and packet header protection cipher context - * object |hp_ctx| to encrypt outgoing Short packets. - * - * If this function succeeds, |conn| takes ownership of |aead_ctx| and - * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and - * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete - * these objects when they are no longer used. If this function - * fails, the caller is responsible to delete them. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int ngtcp2_conn_install_tx_key( - ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, - const ngtcp2_crypto_cipher_ctx *hp_ctx); - -/** - * @function - * - * `ngtcp2_conn_initiate_key_update` initiates the key update. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_INVALID_STATE` - * The previous key update has not been confirmed yet; or key - * update is too frequent; or new keys are not available yet. - */ -NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_set_tls_error` sets the TLS related error in |conn|. - * In general, error code should be propagated via return value, but - * sometimes ngtcp2 API is called inside callback function of TLS - * stack and it does not allow to return ngtcp2 error code directly. - * In this case, implementation can set the error code (e.g., - * NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM) using this function. - */ -NGTCP2_EXTERN void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr); - -/** - * @function - * - * `ngtcp2_conn_get_tls_error` returns the value set by - * `ngtcp2_conn_set_tls_error`. If no value is set, this function - * returns 0. - */ -NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_loss_detection_expiry` returns the expiry time point - * of loss detection timer. Application should call - * `ngtcp2_conn_on_loss_detection_timer` and `ngtcp2_conn_write_pkt` - * (or `ngtcp2_conn_writev_stream`) when it expires. It returns - * UINT64_MAX if loss detection timer is not armed. - */ -NGTCP2_EXTERN ngtcp2_tstamp -ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of - * delayed protected ACK. Application should call - * ngtcp2_conn_cancel_expired_ack_delay_timer() and - * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_writev_stream`) when it - * expires. It returns UINT64_MAX if there is no expiry. - */ -NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_expiry` returns the next expiry time. This - * function returns the timestamp such that - * min(ngtcp2_conn_loss_detection_expiry(conn), - * ngtcp2_conn_ack_delay_expiry(conn), other timers in |conn|). - * - * Call `ngtcp2_conn_handle_expiry()` and `ngtcp2_conn_write_pkt` (or - * `ngtcp2_conn_writev_stream`) if expiry time is passed. - */ -NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_handle_expiry` handles expired timer. It does nothing - * if timer is not expired. - */ -NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_cancel_expired_ack_delay_timer` stops expired ACK - * delay timer. |ts| is the current time. This function must be - * called when ngtcp2_conn_ack_delay_expiry() <= ts. - */ -NGTCP2_EXTERN void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_get_idle_expiry` returns the time when a connection - * should be closed if it continues to be idle. If idle timeout is - * disabled, this function returns UINT64_MAX. - */ -NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_pto` returns Probe Timeout (PTO). - */ -NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_set_remote_transport_params` sets transport parameter - * |params| to |conn|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_PROTO` - * If |conn| is server, and negotiated_version field is not the - * same as the used version. - */ -NGTCP2_EXTERN int -ngtcp2_conn_set_remote_transport_params(ngtcp2_conn *conn, - const ngtcp2_transport_params *params); - -/** - * @function - * - * `ngtcp2_conn_get_remote_transport_params` fills settings values in - * |params|. original_connection_id and - * original_connection_id_present are always zero filled. - */ -NGTCP2_EXTERN void -ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params); - -/** - * @function - * - * `ngtcp2_conn_set_early_remote_transport_params` sets |params| as - * transport parameter previously received from a server. The - * parameters are used to send 0-RTT data. QUIC requires that client - * application should remember transport parameter as well as session - * ticket. - * - * At least following fields must be set: - * - * * initial_max_stream_id_bidi - * * initial_max_stream_id_uni - * * initial_max_stream_data_bidi_local - * * initial_max_stream_data_bidi_remote - * * initial_max_stream_data_uni - * * initial_max_data - */ -NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params); - -/** - * @function - * - * `ngtcp2_conn_set_local_transport_params` sets the local transport - * parameters |params|. This function can only be called by server. - * Although the local transport parameters are passed to - * `ngtcp2_conn_server_new`, server might want to update them after - * ALPN is chosen. In that case, server can update the transport - * parameter with this function. Server must call this function - * before calling `ngtcp2_conn_install_tx_handshake_key`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_INVALID_STATE` - * `ngtcp2_conn_install_tx_handshake_key` has been called. - */ -NGTCP2_EXTERN int -ngtcp2_conn_set_local_transport_params(ngtcp2_conn *conn, - const ngtcp2_transport_params *params); - -/** - * @function - * - * `ngtcp2_conn_get_local_transport_params` fills settings values in - * |params|. - */ -NGTCP2_EXTERN void -ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params); - -/** - * @function - * - * `ngtcp2_conn_open_bidi_stream` opens new bidirectional stream. The - * |stream_user_data| is the user data specific to the stream. The - * open stream ID is stored in |*pstream_id|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_STREAM_ID_BLOCKED` - * The remote peer does not allow |stream_id| yet. - */ -NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, - int64_t *pstream_id, - void *stream_user_data); - -/** - * @function - * - * `ngtcp2_conn_open_uni_stream` opens new unidirectional stream. The - * |stream_user_data| is the user data specific to the stream. The - * open stream ID is stored in |*pstream_id|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_STREAM_ID_BLOCKED` - * The remote peer does not allow |stream_id| yet. - */ -NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, - int64_t *pstream_id, - void *stream_user_data); - -/** - * @function - * - * `ngtcp2_conn_shutdown_stream` closes stream denoted by |stream_id| - * abruptly. |app_error_code| is one of application error codes, and - * indicates the reason of shutdown. Successful call of this function - * does not immediately erase the state of the stream. The actual - * deletion is done when the remote endpoint sends acknowledgement. - * Calling this function is equivalent to call - * `ngtcp2_conn_shutdown_stream_read`, and - * `ngtcp2_conn_shutdown_stream_write` sequentially. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist - */ -NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, - int64_t stream_id, - uint64_t app_error_code); - -/** - * @function - * - * `ngtcp2_conn_shutdown_stream_write` closes write-side of stream - * denoted by |stream_id| abruptly. |app_error_code| is one of - * application error codes, and indicates the reason of shutdown. If - * this function succeeds, no application data is sent to the remote - * endpoint. It discards all data which has not been acknowledged - * yet. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist - */ -NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, - int64_t stream_id, - uint64_t app_error_code); - -/** - * @function - * - * `ngtcp2_conn_shutdown_stream_read` closes read-side of stream - * denoted by |stream_id| abruptly. |app_error_code| is one of - * application error codes, and indicates the reason of shutdown. If - * this function succeeds, no application data is forwarded to an - * application layer. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist - */ -NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, - int64_t stream_id, - uint64_t app_error_code); - -/** - * @enum - * - * ngtcp2_write_stream_flag defines extra behaviour for - * `ngtcp2_conn_writev_stream()`. - */ -typedef enum ngtcp2_write_stream_flag { - NGTCP2_WRITE_STREAM_FLAG_NONE = 0x00, - /** - * NGTCP2_WRITE_STREAM_FLAG_MORE indicates that more stream data may - * come and should be coalesced into the same packet if possible. - */ - NGTCP2_WRITE_STREAM_FLAG_MORE = 0x01, - /** - * NGTCP2_WRITE_STREAM_FLAG_FIN indicates that the passed data is - * the final part of a stream. - */ - NGTCP2_WRITE_STREAM_FLAG_FIN = 0x02 -} ngtcp2_write_stream_flag; - -/** - * @function - * - * `ngtcp2_conn_write_stream` is just like - * `ngtcp2_conn_writev_stream`. The only difference is that it - * conveniently accepts a single buffer. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( - ngtcp2_conn *conn, ngtcp2_path *path, 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); - -/** - * @function - * - * `ngtcp2_conn_writev_stream` writes a packet containing stream data - * of stream denoted by |stream_id|. The buffer of the packet is - * pointed by |dest| of length |destlen|. This function performs QUIC - * handshake as well. - * - * Specifying -1 to |stream_id| means no new stream data to send. - * - * If |path| is not NULL, this function stores the network path with - * which the packet should be sent. Each addr field must point to the - * buffer which is at least 128 bytes. ``sizeof(struct - * sockaddr_storage)`` is enough. The assignment might not be done if - * nothing is written to |dest|. - * - * If the all given data is encoded as STREAM frame in |dest|, and if - * |flags| & NGTCP2_WRITE_STREAM_FLAG_FIN is nonzero, fin flag is set - * to outgoing STREAM frame. Otherwise, fin flag in STREAM frame is - * not set. - * - * This packet may contain frames other than STREAM frame. The packet - * might not contain STREAM frame if other frames occupy the packet. - * In that case, |*pdatalen| would be -1 if |pdatalen| is not NULL. - * - * If |flags| & NGTCP2_WRITE_STREAM_FLAG_FIN is nonzero, and 0 length - * STREAM frame is successfully serialized, |*pdatalen| would be 0. - * - * The number of data encoded in STREAM frame is stored in |*pdatalen| - * if it is not NULL. The caller must keep the portion of data - * covered by |*pdatalen| bytes in tact until - * :type:`ngtcp2_acked_stream_data_offset` indicates that they are - * acknowledged by a remote endpoint or the stream is closed. - * - * If |flags| equals to :enum:`NGTCP2_WRITE_STREAM_FLAG_NONE`, this - * function produces a single payload of UDP packet. If the given - * stream data is small (e.g., few bytes), the packet might be - * severely under filled. Too many small packet might increase - * overall packet processing costs. Unless there are retransmissions, - * by default, application can only send 1 STREAM frame in one QUIC - * packet. In order to include more than 1 STREAM frame in one QUIC - * packet, specify :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|. - * This is analogous to ``MSG_MORE`` flag in ``send(2)``. If the - * :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 - * outcomes: - * - * - The function returns the written length of packet just like - * without :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE`. This is because - * packet is nearly full and the library decided to make a complete - * packet. In this case, |*pdatalen| == -1 is asserted. - * - * - The function returns :enum:`NGTCP2_ERR_WRITE_MORE`. In this - * case, |*pdatalen| >= 0 is asserted. This indicates that - * application can call this function with different stream data to - * pack them into the same packet. Application has to specify the - * same |conn|, |path|, |dest|, |destlen|, |pdatalen|, and |ts| - * parameters, otherwise the behaviour is undefined. The - * application can change |flags|. - * - * - The function returns :enum:`NGTCP2_ERR_STREAM_DATA_BLOCKED` which - * indicates that stream is blocked because of flow control. - * - * - The other error might be returned just like without - * :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE`. - * - * When application sees :enum:`NGTCP2_ERR_WRITE_MORE`, it must not - * call other ngtcp2 API functions (application can still call - * `ngtcp2_conn_write_connection_close` or - * `ngtcp2_conn_write_application_close` to handle error from this - * function). Just keep calling `ngtcp2_conn_writev_stream` or - * `ngtcp2_conn_write_pkt` until it returns a positive number (which - * indicates a complete packet is ready). If |*pdatalen| >= 0, the - * function always return :enum:`NGTCP2_ERR_WRITE_MORE`. - * - * 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. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist - * :enum:`NGTCP2_ERR_STREAM_SHUT_WR` - * Stream is half closed (local); or stream is being reset. - * :enum:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - * :enum:`NGTCP2_ERR_STREAM_DATA_BLOCKED` - * Stream is blocked because of flow control. - * :enum:`NGTCP2_ERR_WRITE_MORE` - * (Only when :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` is specified) - * Application can call this function to pack more stream data - * into the same packet. See above to know how it works. - * - * In general, if the error code which satisfies - * ngtcp2_err_is_fatal(err) != 0 is returned, the application should - * just close the connection by calling - * `ngtcp2_conn_write_connection_close` or just delete the QUIC - * connection using `ngtcp2_conn_del`. It is undefined to call the - * other library functions. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( - ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, - ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, - const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_connection_close` writes a packet which contains - * a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed by - * |dest| whose capacity is |datalen|. - * - * If |path| is not NULL, this function stores the network path with - * which the packet should be sent. Each addr field must point to the - * buffer which is at least 128 bytes. ``sizeof(struct - * sockaddr_storage)`` is enough. The assignment might not be done if - * nothing is written to |dest|. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :enum:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :enum:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close( - ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, - uint64_t error_code, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_application_close` writes a packet which - * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed - * by |dest| whose capacity is |datalen|. - * - * If |path| is not NULL, this function stores the network path with - * which the packet should be sent. Each addr field must point to the - * buffer which is at least 128 bytes. ``sizeof(struct - * sockaddr_storage)`` is enough. The assignment might not be done if - * nothing is written to |dest|. - * - * If handshake has not been confirmed yet, CONNECTION_CLOSE (type - * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written - * instead. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :enum:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :enum:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close( - ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, - uint64_t app_error_code, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in - * closing period. - */ -NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in - * draining period. - */ -NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_extend_max_stream_offset` extends stream's max stream - * data value by |datalen|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream was not found - */ -NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, - int64_t stream_id, - uint64_t datalen); - -/** - * @function - * - * `ngtcp2_conn_extend_max_offset` extends max data offset by - * |datalen|. - */ -NGTCP2_EXTERN void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, - uint64_t datalen); - -/** - * @function - * - * `ngtcp2_conn_extend_max_streams_bidi` extends the number of maximum - * local bidirectional streams that a remote endpoint can open by |n|. - * - * The library does not increase maximum stream limit automatically. - * The exception is when a stream is closed without - * :type:`ngtcp2_stream_open` callback being called. In this case, - * stream limit is increased automatically. - */ -NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, - size_t n); - -/** - * @function - * - * `ngtcp2_conn_extend_max_streams_uni` extends the number of maximum - * local unidirectional streams that a remote endpoint can open by - * |n|. - * - * The library does not increase maximum stream limit automatically. - * The exception is when a stream is closed without - * :type:`ngtcp2_stream_open` callback being called. In this case, - * stream limit is increased automatically. - */ -NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, - size_t n); - -/** - * @function - * - * `ngtcp2_conn_get_dcid` returns the non-NULL pointer to destination - * connection ID. If no destination connection ID is present, the - * return value is not ``NULL``, and its datalen field is 0. - */ -NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_num_scid` returns the number of source connection - * IDs which the local endpoint has provided to the peer and have not - * retired. - */ -NGTCP2_EXTERN size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_scid` writes the all source connection IDs which - * the local endpoint has provided to the peer and have not retired in - * |dest|. The buffer pointed by |dest| must have - * ``sizeof(ngtcp2_cid) * n`` bytes available, where n is the return - * value of `ngtcp2_conn_get_num_scid()`. - */ -NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest); - -/** - * @function - * - * `ngtcp2_conn_get_num_active_dcid` returns the number of the active - * destination connection ID. - */ -NGTCP2_EXTERN size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn); - -/** - * @struct - * - * :type:`ngtcp2_cid_token` is the convenient struct to store - * Connection ID, its associated path, and stateless reset token. - */ -typedef struct ngtcp2_cid_token { - /* seq is the sequence number of this Connection ID. */ - uint64_t seq; - /* cid is Connection ID. */ - ngtcp2_cid cid; - /* ps is the path which is associated to this Connection ID. */ - ngtcp2_path_storage ps; - /* token is the stateless reset token for this Connection ID. */ - uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; - /* token_resent is nonzero if token contains stateless reset - token. */ - uint8_t token_present; -} ngtcp2_cid_token; - -/** - * @function - * - * `ngtcp2_conn_get_active_dcid` writes the all active destination - * connection IDs and tokens to |dest|. The buffer pointed by |dest| - * must have ``sizeof(ngtcp2_cid_token) * n`` bytes available, where n - * is the return value of `ngtcp2_conn_get_num_active_dcid()`. - */ -NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, - ngtcp2_cid_token *dest); - -/** - * @function - * - * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. - */ -NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_early_data_rejected` tells |conn| that 0-RTT data was - * rejected by a server. - */ -NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to - * |*cstat|. - */ -NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, - ngtcp2_conn_stat *cstat); - -/** - * @function - * - * `ngtcp2_conn_on_loss_detection_timer` should be called when a timer - * returned from `ngtcp2_conn_earliest_expiry` fires. - * - * Application should call `ngtcp2_conn_handshake` if handshake has - * not completed, otherwise `ngtcp2_conn_write_pkt` (or - * `ngtcp2_conn_write_stream` if it has data to send) to send TLP/RTO - * probe packets. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - */ -NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_submit_crypto_data` submits crypto stream data |data| - * of length |datalen| to the library for transmission. The - * encryption level is given in |crypto_level|. - * - * Application should keep the buffer pointed by |data| alive until - * the data is acknowledged. The acknowledgement is notified by - * :type:`ngtcp2_acked_crypto_offset` callback. - */ -NGTCP2_EXTERN int -ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - const uint8_t *data, const size_t datalen); - -/** - * @function - * - * `ngtcp2_conn_submit_new_token` submits address validation token. - * It is sent in NEW_TOKEN frame. Only server can call this function. - * |tokenlen| must not be 0. - * - * This function makes a copy of the buffer pointed by |token| of - * length |tokenlen|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory. - */ -NGTCP2_EXTERN int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, - const uint8_t *token, - size_t tokenlen); - -/** - * @function - * - * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to - * |conn|. - */ -NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, - const ngtcp2_addr *addr); - -/** - * @function - * - * `ngtcp2_conn_set_remote_addr` sets remote endpoint address |addr| - * to |conn|. - */ -NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, - const ngtcp2_addr *addr); - -/** - * @function - * - * `ngtcp2_conn_get_remote_addr` returns the remote endpoint address - * set in |conn|. - */ -NGTCP2_EXTERN const ngtcp2_addr *ngtcp2_conn_get_remote_addr(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_initiate_migration` starts connection migration to the - * given |path| which must not be NULL. Only client can initiate - * migration. This function does immediate migration; it does not - * probe peer reachability from a new local address. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_INVALID_STATE` - * Migration is disabled. - * :enum:`NGTCP2_ERR_CONN_ID_BLOCKED` - * No unused connection ID is available. - * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` - * |path| equals the current path. - * :enum:`NGTCP2_ERR_NOMEM` - * Out of memory - */ -NGTCP2_EXTERN int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, - const ngtcp2_path *path, - ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_get_max_local_streams_uni` returns the cumulative - * number of streams which local endpoint can open. - */ -NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_max_data_left` returns the number of bytes that - * this local endpoint can send in this connection. - */ -NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_streams_bidi_left` returns the number of - * bidirectional streams which the local endpoint can open without - * violating stream concurrency limit. - */ -NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_get_streams_uni_left` returns the number of - * unidirectional streams which the local endpoint can open without - * violating stream concurrency limit. - */ -NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_set_initial_crypto_ctx` sets |ctx| for Initial packet - * encryption. The passed data will be passed to - * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and - * :type:`ngtcp2_hp_mask` callbacks. - */ -NGTCP2_EXTERN void -ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, - const ngtcp2_crypto_ctx *ctx); - -/** - * @function - * - * `ngtcp2_conn_get_initial_crypto_ctx` returns - * :type:`ngtcp2_crypto_ctx` object for Initial packet encryption. - */ -NGTCP2_EXTERN const ngtcp2_crypto_ctx * -ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_set_initial_crypto_ctx` sets |ctx| for - * 0RTT/Handshake/Short packet encryption. In other words, this - * crypto context is used for all packets except for Initial packets. - * The passed data will be passed to :type:`ngtcp2_encrypt`, - * :type:`ngtcp2_decrypt` and :type:`ngtcp2_hp_mask` callbacks. - */ -NGTCP2_EXTERN void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, - const ngtcp2_crypto_ctx *ctx); - -/** - * @function - * - * `ngtcp2_conn_get_tls_native_handle` returns TLS native handle set by - * `ngtcp2_conn_set_tls_native_handle()`. - */ -NGTCP2_EXTERN void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_set_tls_native_handle` sets TLS native handle - * |tls_native_handle| to |conn|. Internally, it is used as an opaque - * pointer. - */ -NGTCP2_EXTERN void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, - void *tls_native_handle); - -/** - * @function - * - * `ngtcp2_conn_set_retry_aead` sets |aead| and |aead_ctx| for Retry - * integrity tag verification. |aead| must be AEAD_AES_128_GCM. - * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as - * encryption key. This function must be called if |conn| is - * initialized as client. Server does not verify the tag and has no - * need to call this function. - * - * If this function succeeds, |conn| takes ownership of |aead_ctx|. - * :type:`ngtcp2_delete_crypto_aead_ctx` will be called to delete this - * object when it is no longer used. If this function fails, the - * caller is responsible to delete it. - */ -NGTCP2_EXTERN void -ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_aead_ctx *aead_ctx); - -/** - * @function - * - * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` - * object for 0RTT/Handshake/Short packet encryption. - */ -NGTCP2_EXTERN const ngtcp2_crypto_ctx * -ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn); - -typedef enum ngtcp2_connection_close_error_code_type { - /* NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT indicates the - error code is QUIC transport error code. */ - NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, - /* NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION indicates the - error code is application error code. */ - NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, -} ngtcp2_connection_close_error_code_type; - -typedef struct ngtcp2_connection_close_error_code { - /* error_code is the error code for connection closure. */ - uint64_t error_code; - /* type is the type of error_code. */ - ngtcp2_connection_close_error_code_type type; -} ngtcp2_connection_close_error_code; - -/** - * @function - * - * `ngtcp2_conn_get_connection_close_error_code` stores the received - * connection close error code in |ccec|. - */ -NGTCP2_EXTERN void ngtcp2_conn_get_connection_close_error_code( - ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec); - -/** - * @function - * - * `ngtcp2_conn_is_local_stream` returns nonzero if |stream_id| denotes the - * stream which a local endpoint issues. - */ -NGTCP2_EXTERN int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, - int64_t stream_id); - -/** - * @function - * - * `ngtcp2_conn_is_server` returns nonzero if |conn| is initialized as - * server. - */ -NGTCP2_EXTERN int ngtcp2_conn_is_server(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_after_retry` returns nonzero if |conn| as a client has - * received Retry packet from server and successfully validated it. - */ -NGTCP2_EXTERN int ngtcp2_conn_after_retry(ngtcp2_conn *conn); - -/** - * @function - * - * `ngtcp2_conn_set_stream_user_data` sets |stream_user_data| to the - * stream identified by |stream_id|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist - */ -NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, - int64_t stream_id, - void *stream_user_data); - -/** - * @function - * - * `ngtcp2_strerror` returns the text representation of |liberr|. - */ -NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); - -/** - * @function - * - * `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error. - */ -NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr); - -/** - * @function - * - * `ngtcp2_err_infer_quic_transport_error_code` returns a QUIC - * transport error code which corresponds to |liberr|. - */ -NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); - -/** - * @function - * - * `ngtcp2_addr_init` initializes |dest| with the given arguments and - * returns |dest|. - */ -NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, - const struct sockaddr *addr, - size_t addrlen, void *user_data); - -/** - * @function - * - * `ngtcp2_path_storage_init` initializes |ps| with the given - * arguments. This function copies |local_addr| and |remote_addr|. - */ -NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, - void *local_user_data, - const struct sockaddr *remote_addr, - size_t remote_addrlen, - void *remote_user_data); - -/** - * @function - * - * `ngtcp2_path_storage_zero` initializes |ps| with the zero length - * addresses. - */ -NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps); - -/** - * @function - * - * `ngtcp2_settings_default` initializes |settings| with the default - * values. First this function fills |settings| with 0 and set the - * default value to the following fields: - * - * * cc_algo = NGTCP2_CC_ALGO_CUBIC - * * initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT - * * transport_params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE - * * transport_params.ack_delay_component = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT - * * transport_params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY - * * transport_params.active_connection_id_limit = - * NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT - */ -NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); - -/* - * @function - * - * `ngtcp2_mem_default` returns the default, system standard memory - * allocator. - */ -NGTCP2_EXTERN const ngtcp2_mem *ngtcp2_mem_default(void); - -/** - * @macro - * - * The age of :type:`ngtcp2_info` - */ -#define NGTCP2_VERSION_AGE 1 - -/** - * @struct - * - * This struct is what `ngtcp2_version()` returns. It holds - * information about the particular ngtcp2 version. - */ -typedef struct ngtcp2_info { - /** - * Age of this struct. This instance of ngtcp2 sets it to - * :macro:`NGTCP2_VERSION_AGE` but a future version may bump it and - * add more struct fields at the bottom - */ - int age; - /** - * the :macro:`NGTCP2_VERSION_NUM` number (since age ==1) - */ - int version_num; - /** - * points to the :macro:`NGTCP2_VERSION` string (since age ==1) - */ - const char *version_str; - /* -------- the above fields all exist when age == 1 */ -} ngtcp2_info; - -/** - * @function - * - * Returns a pointer to a ngtcp2_info struct with version information - * about the run-time library in use. The |least_version| argument - * can be set to a 24 bit numerical value for the least accepted - * version number and if the condition is not met, this function will - * return a ``NULL``. Pass in 0 to skip the version checking. - */ -NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version); - -/** - * @function - * - * `ngtcp2_is_bidi_stream` returns nonzero if |stream_id| denotes - * bidirectional stream. - */ -NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id); - -typedef enum { - NGTCP2_LOG_EVENT_NONE, - /* connection (catch-all) event */ - NGTCP2_LOG_EVENT_CON, - /* packet event */ - NGTCP2_LOG_EVENT_PKT, - /* frame event */ - NGTCP2_LOG_EVENT_FRM, - /* recovery event */ - NGTCP2_LOG_EVENT_RCV, - /* crypto event */ - NGTCP2_LOG_EVENT_CRY, - /* path validation event */ - NGTCP2_LOG_EVENT_PTV, -} ngtcp2_log_event; - -/** - * @function - * - * `ngtcp2_log_info` writes info level log. - */ -NGTCP2_EXTERN void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, - const char *fmt, ...); - -#ifdef __cplusplus -} -#endif - -#endif /* NGTCP2_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/lib/includes/ngtcp2/version.h deleted file mode 100644 index 3782959c286..00000000000 --- a/deps/ngtcp2/lib/includes/ngtcp2/version.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2016 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 VERSION_H -#define VERSION_H - -/** - * @macro - * - * Version number of the ngtcp2 library release. - */ -#define NGTCP2_VERSION "0.1.0-DEV" - -/** - * @macro - * - * Numerical representation of the version number of the ngtcp2 - * library 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 NGTCP2_VERSION_NUM 0x000100 - -#endif /* VERSION_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/lib/ngtcp2_acktr.c deleted file mode 100644 index 7a7f3e469a2..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_acktr.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_acktr.h" - -#include <assert.h> - -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) { - *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry)); - if (*ent == NULL) { - return NGTCP2_ERR_NOMEM; - } - - (*ent)->pkt_num = pkt_num; - (*ent)->len = 1; - (*ent)->tstamp = tstamp; - - return 0; -} - -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, ent); -} - -static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { - return *(int64_t *)lhs > *(int64_t *)rhs; -} - -int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, - const ngtcp2_mem *mem) { - int rv; - - rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry), - mem); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); - if (rv != 0) { - ngtcp2_ringbuf_free(&acktr->acks); - return rv; - } - - acktr->log = log; - acktr->mem = mem; - acktr->flags = NGTCP2_ACKTR_FLAG_NONE; - acktr->first_unacked_ts = UINT64_MAX; - acktr->rx_npkt = 0; - - return 0; -} - -void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { - ngtcp2_ksl_it it; - - if (acktr == NULL) { - return; - } - - for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem); - } - ngtcp2_ksl_free(&acktr->ents); - - ngtcp2_ringbuf_free(&acktr->acks); -} - -int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, - ngtcp2_tstamp ts) { - ngtcp2_ksl_it it; - ngtcp2_acktr_entry *ent, *prev_ent, *delent; - int rv; - int added = 0; - - if (ngtcp2_ksl_len(&acktr->ents)) { - it = ngtcp2_ksl_lower_bound(&acktr->ents, &pkt_num); - if (ngtcp2_ksl_it_end(&it)) { - ngtcp2_ksl_it_prev(&it); - ent = ngtcp2_ksl_it_get(&it); - - assert(ent->pkt_num >= pkt_num + (int64_t)ent->len); - - if (ent->pkt_num == pkt_num + (int64_t)ent->len) { - ++ent->len; - added = 1; - } - } else { - ent = ngtcp2_ksl_it_get(&it); - - assert(ent->pkt_num != pkt_num); - - if (ngtcp2_ksl_it_begin(&it)) { - if (ent->pkt_num + 1 == pkt_num) { - ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); - ent->pkt_num = pkt_num; - ent->tstamp = ts; - ++ent->len; - added = 1; - } - } else { - ngtcp2_ksl_it_prev(&it); - prev_ent = ngtcp2_ksl_it_get(&it); - - assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len); - - if (ent->pkt_num + 1 == pkt_num) { - if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { - prev_ent->len += ent->len + 1; - ngtcp2_ksl_remove(&acktr->ents, NULL, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); - added = 1; - } else { - ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); - ent->pkt_num = pkt_num; - ent->tstamp = ts; - ++ent->len; - added = 1; - } - } else if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { - ++prev_ent->len; - added = 1; - } - } - } - } - - if (!added) { - rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem); - if (rv != 0) { - return rv; - } - rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent); - if (rv != 0) { - ngtcp2_acktr_entry_del(ent, acktr->mem); - return rv; - } - } - - if (active_ack) { - acktr->flags |= NGTCP2_ACKTR_FLAG_ACTIVE_ACK; - if (acktr->first_unacked_ts == UINT64_MAX) { - acktr->first_unacked_ts = ts; - } - } - - if (ngtcp2_ksl_len(&acktr->ents) > NGTCP2_ACKTR_MAX_ENT) { - it = ngtcp2_ksl_end(&acktr->ents); - ngtcp2_ksl_it_prev(&it); - delent = ngtcp2_ksl_it_get(&it); - ngtcp2_ksl_remove(&acktr->ents, NULL, &delent->pkt_num); - ngtcp2_acktr_entry_del(delent, acktr->mem); - } - - return 0; -} - -void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { - ngtcp2_ksl_it it; - - it = ngtcp2_ksl_lower_bound(&acktr->ents, &ent->pkt_num); - assert(*(int64_t *)ngtcp2_ksl_it_key(&it) == (int64_t)ent->pkt_num); - - for (; !ngtcp2_ksl_it_end(&it);) { - ent = ngtcp2_ksl_it_get(&it); - ngtcp2_ksl_remove(&acktr->ents, &it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); - } -} - -ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr) { - return ngtcp2_ksl_begin(&acktr->ents); -} - -int ngtcp2_acktr_empty(ngtcp2_acktr *acktr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&acktr->ents); - return ngtcp2_ksl_it_end(&it); -} - -ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, - int64_t pkt_num, - int64_t largest_ack) { - ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks); - - ent->largest_ack = largest_ack; - ent->pkt_num = pkt_num; - - return ent; -} - -/* - * acktr_remove removes |ent| from |acktr|. The iterator which points - * to the entry next to |ent| is assigned to |it|. - */ -static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it, - ngtcp2_acktr_entry *ent) { - ngtcp2_ksl_remove(&acktr->ents, it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); -} - -static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb, - size_t ack_ent_offset) { - ngtcp2_acktr_ack_entry *ack_ent; - ngtcp2_acktr_entry *ent; - ngtcp2_ksl_it it; - - assert(ngtcp2_ringbuf_len(rb)); - - ack_ent = ngtcp2_ringbuf_get(rb, ack_ent_offset); - - /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ - it = ngtcp2_ksl_lower_bound(&acktr->ents, &ack_ent->largest_ack); - for (; !ngtcp2_ksl_it_end(&it);) { - ent = ngtcp2_ksl_it_get(&it); - acktr_remove(acktr, &it, ent); - } - - if (ngtcp2_ksl_len(&acktr->ents)) { - assert(ngtcp2_ksl_it_end(&it)); - - ngtcp2_ksl_it_prev(&it); - ent = ngtcp2_ksl_it_get(&it); - if (ent->pkt_num > ack_ent->largest_ack && - ack_ent->largest_ack >= ent->pkt_num - (int64_t)(ent->len - 1)) { - ent->len = (size_t)(ent->pkt_num - ack_ent->largest_ack); - } - } - - ngtcp2_ringbuf_resize(rb, ack_ent_offset); -} - -void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) { - ngtcp2_acktr_ack_entry *ent; - int64_t largest_ack = fr->largest_ack, min_ack; - size_t i, j; - ngtcp2_ringbuf *rb = &acktr->acks; - size_t nacks = ngtcp2_ringbuf_len(rb); - - /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ - for (j = 0; j < nacks; ++j) { - ent = ngtcp2_ringbuf_get(rb, j); - if (largest_ack >= ent->pkt_num) { - break; - } - } - if (j == nacks) { - return; - } - - min_ack = largest_ack - (int64_t)fr->first_ack_blklen; - - if (min_ack <= ent->pkt_num && ent->pkt_num <= largest_ack) { - acktr_on_ack(acktr, rb, j); - return; - } - - for (i = 0; i < fr->num_blks && j < nacks; ++i) { - largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2; - min_ack = largest_ack - (int64_t)fr->blks[i].blklen; - - for (;;) { - if (ent->pkt_num > largest_ack) { - ++j; - if (j == nacks) { - return; - } - ent = ngtcp2_ringbuf_get(rb, j); - continue; - } - if (ent->pkt_num < min_ack) { - break; - } - acktr_on_ack(acktr, rb, j); - return; - } - } -} - -void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { - acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | - NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK | - NGTCP2_ACKTR_FLAG_CANCEL_TIMER); - acktr->first_unacked_ts = UINT64_MAX; - acktr->rx_npkt = 0; -} - -int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, - ngtcp2_duration max_ack_delay, - ngtcp2_tstamp ts) { - return acktr->first_unacked_ts <= ts - max_ack_delay; -} - -void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { - acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK; -} diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/lib/ngtcp2_acktr.h deleted file mode 100644 index 38c1ebe2cfd..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_acktr.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_ACKTR_H -#define NGTCP2_ACKTR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" -#include "ngtcp2_ringbuf.h" -#include "ngtcp2_ksl.h" -#include "ngtcp2_pkt.h" - -/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry - which ngtcp2_acktr stores. */ -#define NGTCP2_ACKTR_MAX_ENT 1024 - -/* NGTCP2_NUM_IMMEDIATE_ACK_PKT is the maximum number of received - packets which triggers the immediate ACK. */ -#define NGTCP2_NUM_IMMEDIATE_ACK_PKT 2 - -struct ngtcp2_acktr_entry; -typedef struct ngtcp2_acktr_entry ngtcp2_acktr_entry; - -struct ngtcp2_log; -typedef struct ngtcp2_log ngtcp2_log; - -/* - * ngtcp2_acktr_entry is a range of packets which need to be acked. - */ -struct ngtcp2_acktr_entry { - /* pkt_num is the largest packet number to acknowledge in this - range. */ - int64_t pkt_num; - /* len is the consecutive packets started from pkt_num which - includes pkt_num itself counting in decreasing order. So pkt_num - = 987 and len = 2, this entry includes packet 987 and 986. */ - size_t len; - /* tstamp is the timestamp when a packet denoted by pkt_num is - received. */ - ngtcp2_tstamp tstamp; -}; - -/* - * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it - * with the given parameters. The pointer to the allocated object is - * stored to |*ent|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem); - -/* - * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It - * deallocates memory pointed by |ent|. - */ -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem); - -typedef struct { - /* largest_ack is the largest packet number in outgoing ACK frame */ - int64_t largest_ack; - /* pkt_num is the packet number that ACK frame is included. */ - int64_t pkt_num; -} ngtcp2_acktr_ack_entry; - -typedef enum { - NGTCP2_ACKTR_FLAG_NONE = 0x00, - /* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate - acknowledgement is required. */ - NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK = 0x01, - /* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are - pending protected packet to be acknowledged. */ - NGTCP2_ACKTR_FLAG_ACTIVE_ACK = 0x02, - /* NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK is set when server received - acknowledgement for ACK which acknowledges the last handshake - packet from client (which contains TLSv1.3 Finished message). */ - NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK = 0x80, - /* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is - expired and canceled. */ - NGTCP2_ACKTR_FLAG_CANCEL_TIMER = 0x0100, -} ngtcp2_acktr_flag; - -/* - * ngtcp2_acktr tracks received packets which we have to send ack. - */ -typedef struct { - ngtcp2_ringbuf acks; - /* ents includes ngtcp2_acktr_entry sorted by decreasing order of - packet number. */ - ngtcp2_ksl ents; - ngtcp2_log *log; - const ngtcp2_mem *mem; - /* flags is bitwise OR of zero, or more of ngtcp2_ack_flag. */ - uint16_t flags; - /* first_unacked_ts is timestamp when ngtcp2_acktr_entry is added - first time after the last outgoing ACK frame. */ - ngtcp2_tstamp first_unacked_ts; - /* rx_npkt is the number of packets received without sending ACK. */ - size_t rx_npkt; -} ngtcp2_acktr; - -/* - * ngtcp2_acktr_init initializes |acktr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, - const ngtcp2_mem *mem); - -/* - * ngtcp2_acktr_free frees resources allocated for |acktr|. It frees - * any ngtcp2_acktr_entry added to |acktr|. - */ -void ngtcp2_acktr_free(ngtcp2_acktr *acktr); - -/* - * ngtcp2_acktr_add adds packet number |pkt_num| to |acktr|. - * |active_ack| is nonzero if |pkt_num| is retransmittable packet. - * - * This function assumes that |acktr| does not contain |pkt_num|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * OUt of memory. - */ -int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, - ngtcp2_tstamp ts); - -/* - * ngtcp2_acktr_forget removes all entries which have the packet - * number that is equal to or less than ent->pkt_num. This function - * assumes that |acktr| includes |ent|. - */ -void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent); - -/* - * ngtcp2_acktr_get returns the pointer to pointer to the entry which - * has the largest packet number to be acked. If there is no entry, - * returned value satisfies ngtcp2_ksl_it_end(&it) != 0. - */ -ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr); - -/* - * ngtcp2_acktr_empty returns nonzero if it has no packet to - * acknowledge. - */ -int ngtcp2_acktr_empty(ngtcp2_acktr *acktr); - -/* - * ngtcp2_acktr_add_ack records outgoing ACK frame whose largest - * acknowledged packet number is |largest_ack|. |pkt_num| is the - * packet number of a packet in which ACK frame is included. This - * function returns a pointer to the object it adds. - */ -ngtcp2_acktr_ack_entry * -ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, int64_t pkt_num, int64_t largest_ack); - -/* - * ngtcp2_acktr_recv_ack processes the incoming ACK frame |fr|. - * |pkt_num| is a packet number which includes |fr|. If we receive - * ACK which acknowledges the ACKs added by ngtcp2_acktr_add_ack, - * ngtcp2_acktr_entry which the outgoing ACK acknowledges is removed. - */ -void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr); - -/* - * ngtcp2_acktr_commit_ack tells |acktr| that ACK frame is generated. - */ -void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr); - -/* - * ngtcp2_acktr_require_active_ack returns nonzero if ACK frame should - * be generated actively. - */ -int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, - ngtcp2_duration max_ack_delay, - ngtcp2_tstamp ts); - -/* - * ngtcp2_acktr_immediate_ack tells |acktr| that immediate - * acknowledgement is required. - */ -void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr); - -#endif /* NGTCP2_ACKTR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/lib/ngtcp2_addr.c deleted file mode 100644 index cfc91c41e2c..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_addr.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_addr.h" - -#include <string.h> -#include <assert.h> - -#ifdef WIN32 -# include <winsock2.h> -# include <ws2tcpip.h> -#else -# include <sys/types.h> -# include <sys/socket.h> -# include <netinet/in.h> -# include <netinet/ip.h> -# include <netinet/tcp.h> -# include <arpa/inet.h> -#endif - -ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen, void *user_data) { - dest->addrlen = addrlen; - dest->addr = (struct sockaddr *)addr; - dest->user_data = user_data; - return dest; -} - -void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) { - dest->addrlen = src->addrlen; - if (src->addrlen) { - memcpy(dest->addr, src->addr, src->addrlen); - } - dest->user_data = src->user_data; -} - -void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen) { - dest->addrlen = addrlen; - if (addrlen) { - memcpy(dest->addr, addr, addrlen); - } -} - -static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) { - assert(a->sa_family == b->sa_family); - - switch (a->sa_family) { - case AF_INET: { - const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, - *bi = (const struct sockaddr_in *)(void *)b; - return ai->sin_port == bi->sin_port && - memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0; - } - case AF_INET6: { - const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, - *bi = (const struct sockaddr_in6 *)(void *)b; - return ai->sin6_port == bi->sin6_port && - memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0; - } - default: - assert(0); - } -} - -int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { - return a->addr->sa_family == b->addr->sa_family && - sockaddr_eq(a->addr, b->addr); -} - -int ngtcp2_addr_empty(const ngtcp2_addr *addr) { return addr->addrlen == 0; } diff --git a/deps/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/lib/ngtcp2_addr.h deleted file mode 100644 index 238bb435183..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_addr.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_ADDR_H -#define NGTCP2_ADDR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -/* - * ngtcp2_addr_copy copies |src| to |dest|. This function assumes - * that dest->addr points to a buffer which have sufficient size to - * store the copy. - */ -void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src); - -/* - * ngtcp2_addr_copy_byte copies |addr| of length |addrlen| into the - * buffer pointed by dest->addr. dest->len is updated to have - * |addrlen|. This function assumes that dest->addr points to a - * buffer which have sufficient size to store the copy. - */ -void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen); - -/* - * ngtcp2_addr_eq returns nonzero if |a| equals |b|. - */ -int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b); - -/* - * ngtcp2_addr_empty returns nonzero if |addr| has zero length - * address. - */ -int ngtcp2_addr_empty(const ngtcp2_addr *addr); - -#endif /* NGTCP2_ADDR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_buf.c b/deps/ngtcp2/lib/ngtcp2_buf.c deleted file mode 100644 index 373f23d91ae..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_buf.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_buf.h" - -void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) { - buf->begin = buf->pos = buf->last = begin; - buf->end = begin + len; -} - -void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; } - -size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { - return (size_t)(buf->end - buf->last); -} - -size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { - return (size_t)(buf->last - buf->pos); -} - -size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { - return (size_t)(buf->end - buf->begin); -} diff --git a/deps/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/lib/ngtcp2_buf.h deleted file mode 100644 index fe3d06a1c97..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_buf.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_BUF_H -#define NGTCP2_BUF_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -typedef struct { - /* begin points to the beginning of the buffer. */ - uint8_t *begin; - /* end points to the one beyond of the last byte of the buffer */ - uint8_t *end; - /* pos pointers to the start of data. Typically, this points to the - point that next data should be read. Initially, it points to - |begin|. */ - uint8_t *pos; - /* last points to the one beyond of the last data of the buffer. - Typically, new data is written at this point. Initially, it - points to |begin|. */ - uint8_t *last; -} ngtcp2_buf; - -/* - * ngtcp2_buf_init initializes |buf| with the given buffer. - */ -void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len); - -/* - * ngtcp2_buf_reset resets pos and last fields to match begin field to - * make ngtcp2_buf_len(buf) return 0. - */ -void ngtcp2_buf_reset(ngtcp2_buf *buf); - -/* - * ngtcp2_buf_left returns the number of additional bytes which can be - * written to the underlying buffer. In other words, it returns - * buf->end - buf->last. - */ -size_t ngtcp2_buf_left(const ngtcp2_buf *buf); - -/* - * ngtcp2_buf_len returns the number of bytes left to read. In other - * words, it returns buf->last - buf->pos. - */ -size_t ngtcp2_buf_len(const ngtcp2_buf *buf); - -/* - * ngtcp2_buf_cap returns the capacity of the buffer. In other words, - * it returns buf->end - buf->begin. - */ -size_t ngtcp2_buf_cap(const ngtcp2_buf *buf); - -#endif /* NGTCP2_BUF_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/lib/ngtcp2_cc.c deleted file mode 100644 index ef2e63a0efd..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_cc.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_cc.h" - -#include <assert.h> - -#if defined(_MSC_VER) -# include <intrin.h> -#endif - -#include "ngtcp2_log.h" -#include "ngtcp2_macro.h" -#include "ngtcp2_mem.h" -#include "ngtcp2_rcvry.h" - -uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) { - uint64_t n = 2 * max_udp_payload_size; - n = ngtcp2_max(n, 14720); - return ngtcp2_min(10 * max_udp_payload_size, n); -} - -ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, - size_t pktlen, ngtcp2_pktns_id pktns_id, - ngtcp2_tstamp ts_sent) { - pkt->pkt_num = pkt_num; - pkt->pktlen = pktlen; - pkt->pktns_id = pktns_id; - pkt->ts_sent = ts_sent; - - return pkt; -} - -static void reno_cc_reset(ngtcp2_reno_cc *cc) { - cc->max_delivery_rate_sec = 0; - cc->target_cwnd = 0; -} - -void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log) { - cc->ccb.log = log; - reno_cc_reset(cc); -} - -void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc) { (void)cc; } - -int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, - const ngtcp2_mem *mem) { - ngtcp2_reno_cc *reno_cc; - - reno_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_reno_cc)); - if (reno_cc == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_reno_cc_init(reno_cc, log); - - cc->ccb = &reno_cc->ccb; - cc->on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked; - cc->congestion_event = ngtcp2_cc_reno_cc_congestion_event; - cc->on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion; - cc->on_ack_recv = ngtcp2_cc_reno_cc_on_ack_recv; - cc->reset = ngtcp2_cc_reno_cc_reset; - - return 0; -} - -void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { - ngtcp2_reno_cc *reno_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_reno_cc, ccb); - - ngtcp2_reno_cc_free(reno_cc); - ngtcp2_mem_free(mem, reno_cc); -} - -static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, - ngtcp2_tstamp sent_time) { - return sent_time <= cstat->congestion_recovery_start_ts; -} - -void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt, - ngtcp2_tstamp ts) { - ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); - (void)ts; - - if (in_congestion_recovery(cstat, pkt->ts_sent)) { - return; - } - - if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) { - return; - } - - if (cstat->cwnd < cstat->ssthresh) { - cstat->cwnd += pkt->pktlen; - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64, - pkt->pkt_num, cstat->cwnd); - return; - } - - cstat->cwnd += cstat->max_udp_payload_size * pkt->pktlen / cstat->cwnd; -} - -void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, - ngtcp2_tstamp ts) { - ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); - uint64_t min_cwnd; - - if (in_congestion_recovery(cstat, ts_sent)) { - return; - } - - cstat->congestion_recovery_start_ts = ts; - cstat->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS; - min_cwnd = 2 * cstat->max_udp_payload_size; - cstat->cwnd = ngtcp2_max(cstat->cwnd, min_cwnd); - cstat->ssthresh = cstat->cwnd; - - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "reduce cwnd because of packet loss cwnd=%" PRIu64, - cstat->cwnd); -} - -void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - (void)ccx; - (void)ts; - - cstat->cwnd = 2 * cstat->max_udp_payload_size; -} - -void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); - uint64_t target_cwnd, initcwnd; - (void)ts; - - /* TODO Use sliding window for min rtt measurement */ - /* TODO Use sliding window */ - cc->max_delivery_rate_sec = - ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec); - - if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) { - target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS; - initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_udp_payload_size); - cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100; - - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64 - " min_rtt=%" PRIu64, - cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt); - } -} - -void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx) { - ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); - reno_cc_reset(cc); -} - -static void cubic_cc_reset(ngtcp2_cubic_cc *cc) { - cc->max_delivery_rate_sec = 0; - cc->target_cwnd = 0; - cc->w_last_max = 0; - cc->w_tcp = 0; - cc->origin_point = 0; - cc->epoch_start = UINT64_MAX; - cc->k = 0; - - cc->rtt_sample_count = 0; - cc->current_round_min_rtt = UINT64_MAX; - cc->last_round_min_rtt = UINT64_MAX; - cc->window_end = -1; -} - -void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log) { - cc->ccb.log = log; - cubic_cc_reset(cc); -} - -void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc) { (void)cc; } - -int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, - const ngtcp2_mem *mem) { - ngtcp2_cubic_cc *cubic_cc; - - cubic_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_cubic_cc)); - if (cubic_cc == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_cubic_cc_init(cubic_cc, log); - - cc->ccb = &cubic_cc->ccb; - cc->on_pkt_acked = ngtcp2_cc_cubic_cc_on_pkt_acked; - cc->congestion_event = ngtcp2_cc_cubic_cc_congestion_event; - cc->on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion; - cc->on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv; - cc->on_pkt_sent = ngtcp2_cc_cubic_cc_on_pkt_sent; - cc->new_rtt_sample = ngtcp2_cc_cubic_cc_new_rtt_sample; - cc->reset = ngtcp2_cc_cubic_cc_reset; - cc->event = ngtcp2_cc_cubic_cc_event; - - return 0; -} - -void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { - ngtcp2_cubic_cc *cubic_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_cubic_cc, ccb); - - ngtcp2_cubic_cc_free(cubic_cc); - ngtcp2_mem_free(mem, cubic_cc); -} - -static uint64_t ngtcp2_cbrt(uint64_t n) { - int d; - uint64_t a; - - if (n == 0) { - return 0; - } - -#if defined(_MSC_VER) -# if defined(_M_X64) - d = (int)__lzcnt64(n); -# elif defined(_M_ARM64) - { - unsigned long index; - d = sizeof(uint64_t) * CHAR_BIT; - if (_BitScanReverse64(&index, n)) { - d = d - 1 - index; - } - } -# else - if ((n >> 32) != 0) { - d = __lzcnt((unsigned int)(n >> 32)); - } else { - d = 32 + __lzcnt((unsigned int)n); - } -# endif -#else - d = __builtin_clzll(n); -#endif - a = 1ULL << ((64 - d) / 3 + 1); - - for (; a * a * a > n;) { - a = (2 * a + n / a / a) / 3; - } - return a; -} - -/* HyStart++ constants */ -#define NGTCP2_HS_MIN_SSTHRESH 16 -#define NGTCP2_HS_N_RTT_SAMPLE 8 -#define NGTCP2_HS_MIN_ETA (4 * NGTCP2_MILLISECONDS) -#define NGTCP2_HS_MAX_ETA (16 * NGTCP2_MILLISECONDS) - -void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt, - ngtcp2_tstamp ts) { - ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); - ngtcp2_duration t, min_rtt, eta; - uint64_t target; - uint64_t tx, kx, time_delta, delta; - uint64_t add, tcp_add; - uint64_t m; - - if (pkt->pktns_id == NGTCP2_PKTNS_ID_APP && cc->window_end != -1 && - cc->window_end <= pkt->pkt_num) { - cc->window_end = -1; - } - - if (in_congestion_recovery(cstat, pkt->ts_sent)) { - return; - } - - if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) { - return; - } - - if (cstat->cwnd < cstat->ssthresh) { - /* slow-start */ - cstat->cwnd += pkt->pktlen; - - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64, - pkt->pkt_num, cstat->cwnd); - - if (cc->last_round_min_rtt != UINT64_MAX && - cc->current_round_min_rtt != UINT64_MAX && - cstat->cwnd >= NGTCP2_HS_MIN_SSTHRESH * cstat->max_udp_payload_size && - cc->rtt_sample_count >= NGTCP2_HS_N_RTT_SAMPLE) { - eta = cc->last_round_min_rtt / 8; - - if (eta < NGTCP2_HS_MIN_ETA) { - eta = NGTCP2_HS_MIN_ETA; - } else if (eta > NGTCP2_HS_MAX_ETA) { - eta = NGTCP2_HS_MAX_ETA; - } - - if (cc->current_round_min_rtt >= cc->last_round_min_rtt + eta) { - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "HyStart++ exit slow start"); - - cc->w_last_max = cstat->cwnd; - cstat->ssthresh = cstat->cwnd; - } - } - - return; - } - - /* congestion avoidance */ - - if (cc->epoch_start == UINT64_MAX) { - cc->epoch_start = ts; - if (cstat->cwnd < cc->w_last_max) { - cc->k = ngtcp2_cbrt((cc->w_last_max - cstat->cwnd) * 10 / 4 / - cstat->max_udp_payload_size); - cc->origin_point = cc->w_last_max; - } else { - cc->k = 0; - cc->origin_point = cstat->cwnd; - } - - cc->w_tcp = cstat->cwnd; - - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "cubic-ca epoch_start=%" PRIu64 " k=%" PRIu64 - " origin_point=%" PRIu64, - cc->epoch_start, cc->k, cc->origin_point); - - cc->pending_add = 0; - cc->pending_w_add = 0; - } - - min_rtt = cstat->min_rtt == UINT64_MAX ? cstat->initial_rtt : cstat->min_rtt; - - t = ts + min_rtt - cc->epoch_start; - - tx = (t << 4) / NGTCP2_SECONDS; - kx = (cc->k << 4); - - if (tx > kx) { - time_delta = tx - kx; - } else { - time_delta = kx - tx; - } - - delta = cstat->max_udp_payload_size * - ((((time_delta * time_delta) >> 4) * time_delta) >> 8) * 4 / 10; - - if (tx > kx) { - target = cc->origin_point + delta; - } else { - target = cc->origin_point - delta; - } - - if (target > cstat->cwnd) { - m = cc->pending_add + cstat->max_udp_payload_size * (target - cstat->cwnd); - add = m / cstat->cwnd; - cc->pending_add = m % cstat->cwnd; - } else { - m = cc->pending_add + cstat->max_udp_payload_size; - add = m / (100 * cstat->cwnd); - cc->pending_add = m % (100 * cstat->cwnd); - } - - m = cc->pending_w_add + cstat->max_udp_payload_size * pkt->pktlen; - - cc->w_tcp += m / cstat->cwnd; - cc->pending_w_add = m % cstat->cwnd; - - if (cc->w_tcp > cstat->cwnd) { - tcp_add = - cstat->max_udp_payload_size * (cc->w_tcp - cstat->cwnd) / cstat->cwnd; - if (tcp_add > add) { - add = tcp_add; - } - } - - cstat->cwnd += add; - - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " acked, cubic-ca cwnd=%" PRIu64 " t=%" PRIu64 - " k=%" PRIi64 " time_delta=%" PRIu64 " delta=%" PRIu64 - " target=%" PRIu64 " w_tcp=%" PRIu64, - pkt->pkt_num, cstat->cwnd, t, cc->k, time_delta >> 4, delta, - target, cc->w_tcp); -} - -void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, - ngtcp2_tstamp ts) { - ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); - uint64_t min_cwnd; - - if (in_congestion_recovery(cstat, ts_sent)) { - return; - } - - cstat->congestion_recovery_start_ts = ts; - - cc->epoch_start = UINT64_MAX; - if (cstat->cwnd < cc->w_last_max) { - cc->w_last_max = cstat->cwnd * 17 / 10 / 2; - } else { - cc->w_last_max = cstat->cwnd; - } - - min_cwnd = 2 * cstat->max_udp_payload_size; - cstat->ssthresh = cstat->cwnd * 7 / 10; - cstat->ssthresh = ngtcp2_max(cstat->ssthresh, min_cwnd); - cstat->cwnd = cstat->ssthresh; - - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "reduce cwnd because of packet loss cwnd=%" PRIu64, - cstat->cwnd); -} - -void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - (void)ccx; - (void)ts; - - cstat->cwnd = 2 * cstat->max_udp_payload_size; -} - -void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); - uint64_t target_cwnd, initcwnd; - (void)ts; - - /* TODO Use sliding window for min rtt measurement */ - /* TODO Use sliding window */ - cc->max_delivery_rate_sec = - ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec); - - if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) { - target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS; - initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_udp_payload_size); - cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100; - - ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, - "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64 - " min_rtt=%" PRIu64, - cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt); - } -} - -void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt) { - ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); - (void)cstat; - - if (pkt->pktns_id != NGTCP2_PKTNS_ID_APP || cc->window_end != -1) { - return; - } - - cc->window_end = pkt->pkt_num; - cc->last_round_min_rtt = cc->current_round_min_rtt; - cc->current_round_min_rtt = UINT64_MAX; - cc->rtt_sample_count = 0; -} - -void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); - (void)ts; - - if (cc->window_end == -1) { - return; - } - - cc->current_round_min_rtt = - ngtcp2_min(cc->current_round_min_rtt, cstat->latest_rtt); - ++cc->rtt_sample_count; -} - -void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx) { - ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); - cubic_cc_reset(cc); -} - -void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_cc_event_type event, ngtcp2_tstamp ts) { - ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); - ngtcp2_tstamp last_ts; - - if (event != NGTCP2_CC_EVENT_TYPE_TX_START || cc->epoch_start == UINT64_MAX) { - return; - } - - last_ts = cstat->last_tx_pkt_ts[NGTCP2_PKTNS_ID_APP]; - if (last_ts == UINT64_MAX || last_ts <= cc->epoch_start) { - return; - } - - assert(ts >= last_ts); - - cc->epoch_start += ts - last_ts; -} diff --git a/deps/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/lib/ngtcp2_cc.h deleted file mode 100644 index 05010d57251..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_cc.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_CC_H -#define NGTCP2_CC_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#define NGTCP2_LOSS_REDUCTION_FACTOR_BITS 1 -#define NGTCP2_PERSISTENT_CONGESTION_THRESHOLD 3 - -struct ngtcp2_log; -typedef struct ngtcp2_log ngtcp2_log; - -/* - * ngtcp2_cc_compute_initcwnd computes initial cwnd. - */ -uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size); - -ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, - size_t pktlen, ngtcp2_pktns_id pktns_id, - ngtcp2_tstamp ts_sent); - -/* ngtcp2_reno_cc is the RENO congestion controller. */ -struct ngtcp2_reno_cc { - ngtcp2_cc_base ccb; - uint64_t max_delivery_rate_sec; - uint64_t target_cwnd; -}; - -typedef struct ngtcp2_reno_cc ngtcp2_reno_cc; - -int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, - const ngtcp2_mem *mem); - -void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); - -void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log); - -void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc); - -void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); - -void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, - ngtcp2_tstamp ts); - -void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc); - -/* ngtcp2_cubic_cc is CUBIC congestion controller. */ -typedef struct ngtcp2_cubic_cc { - ngtcp2_cc_base ccb; - uint64_t max_delivery_rate_sec; - uint64_t target_cwnd; - uint64_t w_last_max; - uint64_t w_tcp; - uint64_t origin_point; - ngtcp2_tstamp epoch_start; - uint64_t k; - /* HyStart++ variables */ - size_t rtt_sample_count; - uint64_t current_round_min_rtt; - uint64_t last_round_min_rtt; - int64_t window_end; - uint64_t pending_add; - uint64_t pending_w_add; -} ngtcp2_cubic_cc; - -int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, - const ngtcp2_mem *mem); - -void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); - -void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log); - -void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc); - -void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt, - ngtcp2_tstamp ts); - -void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, - ngtcp2_tstamp ts); - -void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt); - -void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc); - -void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_cc_event_type event, ngtcp2_tstamp ts); - -#endif /* NGTCP2_CC_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/lib/ngtcp2_cid.c deleted file mode 100644 index 126e3c4a512..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_cid.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_cid.h" - -#include <assert.h> -#include <string.h> - -#include "ngtcp2_path.h" -#include "ngtcp2_str.h" - -void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; } - -void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) { - assert(datalen <= NGTCP2_MAX_CIDLEN); - - cid->datalen = datalen; - if (datalen) { - ngtcp2_cpymem(cid->data, data, datalen); - } -} - -int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other) { - return cid->datalen == other->datalen && - 0 == memcmp(cid->data, other->data, cid->datalen); -} - -int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) { - int s = lhs->datalen < rhs->datalen; - size_t n = s ? lhs->datalen : rhs->datalen; - int c = memcmp(lhs->data, rhs->data, n); - - return c < 0 || (c == 0 && s); -} - -int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; } - -void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token) { - scid->pe.index = NGTCP2_PQ_BAD_INDEX; - scid->seq = seq; - scid->cid = *cid; - scid->ts_retired = UINT64_MAX; - scid->flags = NGTCP2_SCID_FLAG_NONE; - if (token) { - memcpy(scid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); - } else { - memset(scid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); - } -} - -void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) { - ngtcp2_scid_init(dest, src->seq, &src->cid, src->token); - dest->ts_retired = src->ts_retired; - dest->flags = src->flags; -} - -void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token) { - dcid->seq = seq; - dcid->cid = *cid; - if (token) { - memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); - } else { - memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); - } - ngtcp2_path_storage_zero(&dcid->ps); - dcid->ts_retired = UINT64_MAX; -} - -void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { - ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token); - ngtcp2_path_copy(&dest->ps.path, &src->ps.path); - dest->ts_retired = src->ts_retired; -} - -void ngtcp2_dcid_copy_no_path(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { - dest->seq = src->seq; - dest->cid = src->cid; - memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); - - dest->ts_retired = src->ts_retired; -} - -int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, - const ngtcp2_cid *cid, const uint8_t *token) { - if (dcid->seq == seq) { - return ngtcp2_cid_eq(&dcid->cid, cid) && - memcmp(dcid->token, token, - NGTCP2_STATELESS_RESET_TOKENLEN) == 0 - ? 0 - : NGTCP2_ERR_PROTO; - } - - return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO; -} diff --git a/deps/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/lib/ngtcp2_cid.h deleted file mode 100644 index fe5576ae468..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_cid.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_CID_H -#define NGTCP2_CID_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_pq.h" -#include "ngtcp2_path.h" - -typedef enum { - NGTCP2_SCID_FLAG_NONE, - NGTCP2_SCID_FLAG_USED = 0x01, - NGTCP2_SCID_FLAG_RETIRED = 0x02, -} ngtcp2_scid_flag; - -typedef struct { - ngtcp2_pq_entry pe; - /* seq is the sequence number associated to the CID. */ - uint64_t seq; - /* cid is a connection ID */ - ngtcp2_cid cid; - /* ts_retired is the timestamp when peer tells that this CID is - retired. */ - ngtcp2_tstamp ts_retired; - /* flags is the bitwise OR of zero or more of ngtcp2_scid_flag. */ - uint8_t flags; - /* token is a stateless reset token associated to this CID. - Actually, the stateless reset token is tied to the connection, - not to the particular connection ID. */ - uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; -} ngtcp2_scid; - -typedef struct { - /* seq is the sequence number associated to the CID. */ - uint64_t seq; - /* cid is a connection ID */ - ngtcp2_cid cid; - /* path is a path which cid is bound to. The addresses are zero - length if cid has not been bound to a particular path yet. */ - ngtcp2_path_storage ps; - /* ts_retired is the timestamp when peer tells that this CID is - retired. */ - ngtcp2_tstamp ts_retired; - /* token is a stateless reset token associated to this CID. - Actually, the stateless reset token is tied to the connection, - not to the particular connection ID. */ - uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; -} ngtcp2_dcid; - -/* ngtcp2_cid_zero makes |cid| zero-length. */ -void ngtcp2_cid_zero(ngtcp2_cid *cid); - -/* - * ngtcp2_cid_eq returns nonzero if |cid| and |other| share the same - * connection ID. - */ -int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other); - -/* - * ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller - * than |rhs|. - */ -int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs); - -/* - * ngtcp2_cid_empty returns nonzero if |cid| includes empty connection - * ID. - */ -int ngtcp2_cid_empty(const ngtcp2_cid *cid); - -/* - * ngtcp2_scid_init initializes |scid| with the given parameters. If - * |token| is NULL, the function fills scid->token it with 0. |token| - * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. - */ -void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token); - -/* - * ngtcp2_scid_copy copies |src| into |dest|. - */ -void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src); - -/* - * ngtcp2_dcid_init initializes |dcid| with the given parameters. If - * |token| is NULL, the function fills dcid->token it with 0. |token| - * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. - */ -void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token); - -/* - * ngtcp2_dcid_copy copies |src| into |dest|. - */ -void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src); - -/* - * ngtcp2_dcid_copy_no_path behaves like ngtcp2_dcid_copy, but it does - * not copy path. - */ -void ngtcp2_dcid_copy_no_path(ngtcp2_dcid *dest, const ngtcp2_dcid *src); - -/* - * ngtcp2_dcid_verify_uniqueness verifies uniqueness of (|seq|, |cid|, - * |token|) tuple against |dcid|. - */ -int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, - const ngtcp2_cid *cid, const uint8_t *token); - -#endif /* NGTCP2_CID_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/lib/ngtcp2_conn.c deleted file mode 100644 index ff8c608b776..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_conn.c +++ /dev/null @@ -1,10100 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_conn.h" - -#include <string.h> -#include <assert.h> -#include <math.h> - -#include "ngtcp2_macro.h" -#include "ngtcp2_log.h" -#include "ngtcp2_cid.h" -#include "ngtcp2_conv.h" -#include "ngtcp2_vec.h" -#include "ngtcp2_addr.h" -#include "ngtcp2_path.h" -#include "ngtcp2_rcvry.h" - -/* - * conn_local_stream returns nonzero if |stream_id| indicates that it - * is the stream initiated by local endpoint. - */ -static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) { - return (uint8_t)(stream_id & 1) == conn->server; -} - -/* - * bidi_stream returns nonzero if |stream_id| is a bidirectional - * stream ID. - */ -static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; } - -static int conn_call_recv_client_initial(ngtcp2_conn *conn, - const ngtcp2_cid *dcid) { - int rv; - - assert(conn->callbacks.recv_client_initial); - - rv = conn->callbacks.recv_client_initial(conn, dcid, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_handshake_completed(ngtcp2_conn *conn) { - int rv; - - if (!conn->callbacks.handshake_completed) { - return 0; - } - - rv = conn->callbacks.handshake_completed(conn, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_recv_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint32_t flags, uint64_t offset, - const uint8_t *data, size_t datalen) { - int rv; - - if (!conn->callbacks.recv_stream_data) { - return 0; - } - - rv = conn->callbacks.recv_stream_data(conn, flags, strm->stream_id, offset, - data, datalen, conn->user_data, - strm->stream_user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_recv_crypto_data(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, const uint8_t *data, - size_t datalen) { - int rv; - - assert(conn->callbacks.recv_crypto_data); - - rv = conn->callbacks.recv_crypto_data(conn, crypto_level, offset, data, - datalen, conn->user_data); - switch (rv) { - case 0: - case NGTCP2_ERR_CRYPTO: - case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: - case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: - case NGTCP2_ERR_TRANSPORT_PARAM: - case NGTCP2_ERR_PROTO: - case NGTCP2_ERR_CALLBACK_FAILURE: - return rv; - default: - return NGTCP2_ERR_CALLBACK_FAILURE; - } -} - -static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) { - int rv; - - if (!conn->callbacks.stream_open) { - return 0; - } - - rv = conn->callbacks.stream_open(conn, strm->stream_id, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { - int rv; - - if (!conn->callbacks.stream_close) { - return 0; - } - - rv = conn->callbacks.stream_close(conn, strm->stream_id, app_error_code, - conn->user_data, strm->stream_user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_stream_reset(ngtcp2_conn *conn, int64_t stream_id, - uint64_t final_size, uint64_t app_error_code, - void *stream_user_data) { - int rv; - - if (!conn->callbacks.stream_reset) { - return 0; - } - - rv = conn->callbacks.stream_reset(conn, stream_id, final_size, app_error_code, - conn->user_data, stream_user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_extend_max_local_streams_bidi(ngtcp2_conn *conn, - uint64_t max_streams) { - int rv; - - if (!conn->callbacks.extend_max_local_streams_bidi) { - return 0; - } - - rv = conn->callbacks.extend_max_local_streams_bidi(conn, max_streams, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_extend_max_local_streams_uni(ngtcp2_conn *conn, - uint64_t max_streams) { - int rv; - - if (!conn->callbacks.extend_max_local_streams_uni) { - return 0; - } - - rv = conn->callbacks.extend_max_local_streams_uni(conn, max_streams, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, - uint8_t *token, size_t cidlen) { - int rv; - - assert(conn->callbacks.get_new_connection_id); - - rv = conn->callbacks.get_new_connection_id(conn, cid, token, cidlen, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_remove_connection_id(ngtcp2_conn *conn, - const ngtcp2_cid *cid) { - int rv; - - if (!conn->callbacks.remove_connection_id) { - return 0; - } - - rv = conn->callbacks.remove_connection_id(conn, cid, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path, - ngtcp2_path_validation_result res) { - int rv; - - if (!conn->callbacks.path_validation) { - return 0; - } - - rv = conn->callbacks.path_validation(conn, path, res, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_select_preferred_addr(ngtcp2_conn *conn, - ngtcp2_addr *dest) { - int rv; - - if (!conn->callbacks.select_preferred_addr) { - return 0; - } - - assert(conn->remote.transport_params.preferred_address_present); - - rv = conn->callbacks.select_preferred_addr( - conn, dest, &conn->remote.transport_params.preferred_address, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_extend_max_remote_streams_bidi(ngtcp2_conn *conn, - uint64_t max_streams) { - int rv; - - if (!conn->callbacks.extend_max_remote_streams_bidi) { - return 0; - } - - rv = conn->callbacks.extend_max_remote_streams_bidi(conn, max_streams, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_extend_max_remote_streams_uni(ngtcp2_conn *conn, - uint64_t max_streams) { - int rv; - - if (!conn->callbacks.extend_max_remote_streams_uni) { - return 0; - } - - rv = conn->callbacks.extend_max_remote_streams_uni(conn, max_streams, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_extend_max_stream_data(ngtcp2_conn *conn, - ngtcp2_strm *strm, - int64_t stream_id, - uint64_t datalen) { - int rv; - - if (!conn->callbacks.extend_max_stream_data) { - return 0; - } - - rv = conn->callbacks.extend_max_stream_data( - conn, stream_id, datalen, conn->user_data, strm->stream_user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_dcid_status(ngtcp2_conn *conn, - ngtcp2_connection_id_status_type type, - const ngtcp2_dcid *dcid) { - int rv; - - if (!conn->callbacks.dcid_status) { - return 0; - } - - rv = conn->callbacks.dcid_status( - conn, (int)type, dcid->seq, &dcid->cid, - ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL - : dcid->token, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_activate_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) { - return conn_call_dcid_status(conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, - dcid); -} - -static int conn_call_deactivate_dcid(ngtcp2_conn *conn, - const ngtcp2_dcid *dcid) { - return conn_call_dcid_status( - conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid); -} - -static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn, - ngtcp2_crypto_aead_ctx *aead_ctx) { - if (!aead_ctx->native_handle) { - return; - } - - assert(conn->callbacks.delete_crypto_aead_ctx); - - conn->callbacks.delete_crypto_aead_ctx(conn, aead_ctx, conn->user_data); -} - -static void -conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn, - ngtcp2_crypto_cipher_ctx *cipher_ctx) { - if (!cipher_ctx->native_handle) { - return; - } - - assert(conn->callbacks.delete_crypto_cipher_ctx); - - conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data); -} - -static int crypto_offset_less(const ngtcp2_ksl_key *lhs, - const ngtcp2_ksl_key *rhs) { - return *(int64_t *)lhs < *(int64_t *)rhs; -} - -static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, - ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { - int rv; - - memset(pktns, 0, sizeof(*pktns)); - - rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem); - if (rv != 0) { - return rv; - } - - pktns->tx.last_pkt_num = -1; - pktns->rx.max_pkt_num = -1; - - rv = ngtcp2_acktr_init(&pktns->acktr, log, mem); - if (rv != 0) { - goto fail_acktr_init; - } - - rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, - NULL, mem); - if (rv != 0) { - goto fail_crypto_init; - } - - rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, - sizeof(uint64_t), mem); - if (rv != 0) { - goto fail_tx_frq_init; - } - - ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log, - qlog, mem); - - return 0; - -fail_tx_frq_init: - ngtcp2_strm_free(&pktns->crypto.strm); -fail_crypto_init: - ngtcp2_acktr_free(&pktns->acktr); -fail_acktr_init: - ngtcp2_gaptr_free(&pktns->rx.pngap); - - return rv; -} - -static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, - ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { - int rv; - - *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); - if (*ppktns == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem); - if (rv != 0) { - ngtcp2_mem_free(mem, *ppktns); - } - - return rv; -} - -static int cycle_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) { - ngtcp2_strm *ls = ngtcp2_struct_of(lhs, ngtcp2_strm, pe); - ngtcp2_strm *rs = ngtcp2_struct_of(rhs, ngtcp2_strm, pe); - - if (ls->cycle == rs->cycle) { - return ls->stream_id < rs->stream_id; - } - - return rs->cycle - ls->cycle <= 1; -} - -static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { - ngtcp2_pkt_chain *next; - - for (; pc;) { - next = pc->next; - ngtcp2_pkt_chain_del(pc, mem); - pc = next; - } -} - -static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { - ngtcp2_frame_chain *frc; - ngtcp2_ksl_it it; - - delete_buffed_pkts(pktns->rx.buffed_pkts, mem); - - ngtcp2_frame_chain_list_del(pktns->tx.frq, mem); - - ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem); - ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem); - - for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, mem); - } - - ngtcp2_ksl_free(&pktns->crypto.tx.frq); - ngtcp2_rtb_free(&pktns->rtb); - ngtcp2_strm_free(&pktns->crypto.strm); - ngtcp2_acktr_free(&pktns->acktr); - ngtcp2_gaptr_free(&pktns->rx.pngap); -} - -static void pktns_del(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { - if (pktns == NULL) { - return; - } - - pktns_free(pktns, mem); - - ngtcp2_mem_free(mem, pktns); -} - -static void cc_del(ngtcp2_cc *cc, ngtcp2_cc_algo cc_algo, - const ngtcp2_mem *mem) { - switch (cc_algo) { - case NGTCP2_CC_ALGO_RENO: - ngtcp2_cc_reno_cc_free(cc, mem); - break; - case NGTCP2_CC_ALGO_CUBIC: - ngtcp2_cc_cubic_cc_free(cc, mem); - break; - default: - break; - } -} - -static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { - return ngtcp2_cid_less(lhs, rhs); -} - -static int ts_retired_less(const ngtcp2_pq_entry *lhs, - const ngtcp2_pq_entry *rhs) { - const ngtcp2_scid *a = ngtcp2_struct_of(lhs, ngtcp2_scid, pe); - const ngtcp2_scid *b = ngtcp2_struct_of(rhs, ngtcp2_scid, pe); - - return a->ts_retired < b->ts_retired; -} - -/* - * conn_reset_conn_stat_cc resets congestion state in |cstat|. - */ -static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, - ngtcp2_conn_stat *cstat) { - cstat->latest_rtt = 0; - cstat->min_rtt = UINT64_MAX; - cstat->smoothed_rtt = conn->local.settings.initial_rtt; - cstat->rttvar = conn->local.settings.initial_rtt / 2; - cstat->pto_count = 0; - cstat->loss_detection_timer = 0; - cstat->cwnd = - ngtcp2_cc_compute_initcwnd(conn->local.settings.max_udp_payload_size); - cstat->ssthresh = UINT64_MAX; - cstat->congestion_recovery_start_ts = 0; - cstat->bytes_in_flight = 0; - cstat->delivery_rate_sec = 0; - cstat->recv_rate_sec = 0; -} - -/* - * reset_conn_stat_recovery resets the fields related to the recovery - * function - */ -static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) { - // Initializes them with UINT64_MAX. - memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time)); - memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts)); -} - -/* - * conn_reset_conn_stat resets |cstat|. The following fields are not - * reset: initial_rtt, max_udp_payload_size, bytes_sent, and - * bytes_recv. - */ -static void conn_reset_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) { - conn_reset_conn_stat_cc(conn, cstat); - reset_conn_stat_recovery(cstat); -} - -static void conn_reset_rx_rate(ngtcp2_conn *conn) { - conn->rx.rate.start_ts = UINT64_MAX; - conn->rx.rate.received = 0; -} - -static void conn_update_recv_rate(ngtcp2_conn *conn, size_t datalen, - ngtcp2_tstamp ts) { - uint64_t bps; - ngtcp2_duration window; - - conn->rx.rate.received += datalen; - - if (conn->rx.rate.start_ts == UINT64_MAX) { - conn->rx.rate.start_ts = ts; - return; - } - - assert(conn->cstat.min_rtt); - - window = conn->cstat.min_rtt == UINT64_MAX ? conn->cstat.initial_rtt - : conn->cstat.min_rtt * 2; - - /* If settings.initial_rtt is zero for whatever reason then window - can be zero and we can end up with a division by zero error when - bps is set below. If this assert fails, check that - settings.initial_rtt is not zero. */ - assert(window); - - if (window > ts - conn->rx.rate.start_ts) { - return; - } - - bps = conn->rx.rate.received * NGTCP2_SECONDS / (ts - conn->rx.rate.start_ts); - - if (conn->cstat.recv_rate_sec == 0) { - conn->cstat.recv_rate_sec = bps; - } else { - conn->cstat.recv_rate_sec = (conn->cstat.recv_rate_sec * 3 + bps) / 4; - } - - conn_reset_rx_rate(conn); - - if (conn->cstat.min_rtt != UINT64_MAX) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "recv_rate_sec=%" PRIu64 " bytes/min_rtt=%" PRIu64, - conn->cstat.recv_rate_sec, - conn->cstat.recv_rate_sec * conn->cstat.min_rtt / - NGTCP2_SECONDS); - } -} - -static void delete_scid(ngtcp2_ksl *scids, const ngtcp2_mem *mem) { - ngtcp2_ksl_it it; - - for (it = ngtcp2_ksl_begin(scids); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - ngtcp2_mem_free(mem, ngtcp2_ksl_it_get(&it)); - } -} - -static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_conn_callbacks *callbacks, - const ngtcp2_settings *settings, const ngtcp2_mem *mem, - void *user_data, int server) { - int rv; - ngtcp2_scid *scident; - const ngtcp2_transport_params *params = &settings->transport_params; - uint8_t *buf; - - assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); - - if (mem == NULL) { - mem = ngtcp2_mem_default(); - } - - *pconn = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_conn)); - if (*pconn == NULL) { - rv = NGTCP2_ERR_NOMEM; - goto fail_conn; - } - - rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_unused_init; - } - - rv = - ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_retired_init; - } - - rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); - if (rv != 0) { - goto fail_seqgap_init; - } - - rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); - if (rv != 0) { - goto fail_scid_set_init; - } - - ngtcp2_pq_init(&(*pconn)->scid.used, ts_retired_less, mem); - - rv = ngtcp2_map_init(&(*pconn)->strms, mem); - if (rv != 0) { - goto fail_strms_init; - } - - ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); - - rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_bidi_idtr_init; - } - - rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_uni_idtr_init; - } - - rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4, - sizeof(ngtcp2_path_challenge_entry), mem); - if (rv != 0) { - goto fail_rx_path_challenge_init; - } - - ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf, - settings->initial_ts, user_data); - ngtcp2_qlog_init(&(*pconn)->qlog, settings->qlog.write, settings->initial_ts, - user_data); - if ((*pconn)->qlog.write) { - buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN); - if (buf == NULL) { - goto fail_qlog_buf; - } - ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN); - } - - (*pconn)->local.settings = *settings; - - if (settings->token.len) { - buf = ngtcp2_mem_malloc(mem, settings->token.len); - if (buf == NULL) { - goto fail_token; - } - memcpy(buf, settings->token.base, settings->token.len); - (*pconn)->local.settings.token.base = buf; - } else { - (*pconn)->local.settings.token.base = NULL; - (*pconn)->local.settings.token.len = 0; - } - - if (settings->max_udp_payload_size == 0) { - (*pconn)->local.settings.max_udp_payload_size = NGTCP2_DEFAULT_MAX_PKTLEN; - } - - conn_reset_conn_stat(*pconn, &(*pconn)->cstat); - (*pconn)->cstat.initial_rtt = settings->initial_rtt; - (*pconn)->cstat.max_udp_payload_size = - (*pconn)->local.settings.max_udp_payload_size; - - ngtcp2_rst_init(&(*pconn)->rst); - - (*pconn)->cc_algo = settings->cc_algo; - - switch (settings->cc_algo) { - case NGTCP2_CC_ALGO_RENO: - rv = ngtcp2_cc_reno_cc_init(&(*pconn)->cc, &(*pconn)->log, mem); - if (rv != 0) { - goto fail_cc_init; - } - break; - case NGTCP2_CC_ALGO_CUBIC: - rv = ngtcp2_cc_cubic_cc_init(&(*pconn)->cc, &(*pconn)->log, mem); - if (rv != 0) { - goto fail_cc_init; - } - break; - case NGTCP2_CC_ALGO_CUSTOM: - assert(settings->cc); - (*pconn)->cc = *settings->cc; - (*pconn)->cc.ccb->log = &(*pconn)->log; - break; - default: - assert(0); - } - - conn_reset_rx_rate(*pconn); - - rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); - if (rv != 0) { - goto fail_in_pktns_init; - } - - rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); - if (rv != 0) { - goto fail_hs_pktns_init; - } - - rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APP, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); - if (rv != 0) { - goto fail_pktns_init; - } - - scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); - if (scident == NULL) { - rv = NGTCP2_ERR_NOMEM; - goto fail_scident; - } - - /* Set stateless reset token later if it is available in the local - transport parameters */ - ngtcp2_scid_init(scident, 0, scid, NULL); - - rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident); - if (rv != 0) { - goto fail_scid_set_insert; - } - - scident = NULL; - - ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); - ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path); - - rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1); - if (rv != 0) { - goto fail_seqgap_push; - } - - (*pconn)->server = server; - (*pconn)->oscid = *scid; - (*pconn)->callbacks = *callbacks; - (*pconn)->version = version; - (*pconn)->mem = mem; - (*pconn)->user_data = user_data; - (*pconn)->idle_ts = settings->initial_ts; - (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; - - ngtcp2_qlog_start(&(*pconn)->qlog, server ? &settings->qlog.odcid : dcid, - server); - - return 0; - -fail_seqgap_push: -fail_scid_set_insert: - ngtcp2_mem_free(mem, scident); -fail_scident: - ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); -fail_token: - pktns_free(&(*pconn)->pktns, mem); -fail_pktns_init: - pktns_del((*pconn)->hs_pktns, mem); -fail_hs_pktns_init: - pktns_del((*pconn)->in_pktns, mem); -fail_in_pktns_init: - cc_del(&(*pconn)->cc, settings->cc_algo, mem); -fail_cc_init: - ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin); -fail_qlog_buf: - ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge); -fail_rx_path_challenge_init: - ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr); -fail_remote_uni_idtr_init: - ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr); -fail_remote_bidi_idtr_init: - ngtcp2_map_free(&(*pconn)->strms); -fail_strms_init: - delete_scid(&(*pconn)->scid.set, mem); - ngtcp2_ksl_free(&(*pconn)->scid.set); -fail_scid_set_init: - ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap); -fail_seqgap_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.retired); -fail_dcid_retired_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.unused); -fail_dcid_unused_init: - ngtcp2_mem_free(mem, *pconn); -fail_conn: - return rv; -} - -int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, - const ngtcp2_conn_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_mem *mem, void *user_data) { - int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, mem, - user_data, 0); - if (rv != 0) { - return rv; - } - (*pconn)->rcid = *dcid; - (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL; - (*pconn)->local.bidi.next_stream_id = 0; - (*pconn)->local.uni.next_stream_id = 2; - - rv = ngtcp2_conn_commit_local_transport_params(*pconn); - if (rv != 0) { - ngtcp2_conn_del(*pconn); - return rv; - } - - ngtcp2_qlog_parameters_set_transport_params( - &(*pconn)->qlog, &(*pconn)->local.settings.transport_params, - (*pconn)->server, NGTCP2_QLOG_SIDE_LOCAL); - - return 0; -} - -int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, - const ngtcp2_conn_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_mem *mem, void *user_data) { - int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, mem, - user_data, 1); - if (rv != 0) { - return rv; - } - (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; - (*pconn)->local.bidi.next_stream_id = 1; - (*pconn)->local.uni.next_stream_id = 3; - - if ((*pconn)->local.settings.token.len) { - /* Usage of token lifts amplification limit */ - (*pconn)->flags |= NGTCP2_CONN_FLAG_SADDR_VERIFIED; - } - - return 0; -} - -/* - * conn_fc_credits returns the number of bytes allowed to be sent to - * the given stream. Both connection and stream level flow control - * credits are considered. - */ -static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) { - return ngtcp2_min(strm->tx.max_offset - strm->tx.offset, - conn->tx.max_offset - conn->tx.offset); -} - -/* - * conn_enforce_flow_control returns the number of bytes allowed to be - * sent to the given stream. |len| might be shorted because of - * available flow control credits. - */ -static size_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, - size_t len) { - uint64_t fc_credits = conn_fc_credits(conn, strm); - return (size_t)ngtcp2_min((uint64_t)len, fc_credits); -} - -static int delete_strms_each(ngtcp2_map_entry *ent, void *ptr) { - const ngtcp2_mem *mem = ptr; - ngtcp2_strm *s = ngtcp2_struct_of(ent, ngtcp2_strm, me); - - ngtcp2_strm_free(s); - ngtcp2_mem_free(mem, s); - - return 0; -} - -void ngtcp2_conn_del(ngtcp2_conn *conn) { - if (conn == NULL) { - return; - } - - ngtcp2_qlog_end(&conn->qlog); - - if (conn->early.ckm) { - conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx); - } - conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx); - - if (conn->crypto.key_update.old_rx_ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx); - } - if (conn->crypto.key_update.new_rx_ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->crypto.key_update.new_rx_ckm->aead_ctx); - } - if (conn->crypto.key_update.new_tx_ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->crypto.key_update.new_tx_ckm->aead_ctx); - } - - if (conn->pktns.crypto.rx.ckm) { - conn_call_delete_crypto_aead_ctx(conn, - &conn->pktns.crypto.rx.ckm->aead_ctx); - } - conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.rx.hp_ctx); - - if (conn->pktns.crypto.tx.ckm) { - conn_call_delete_crypto_aead_ctx(conn, - &conn->pktns.crypto.tx.ckm->aead_ctx); - } - conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.tx.hp_ctx); - - if (conn->hs_pktns) { - if (conn->hs_pktns->crypto.rx.ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->hs_pktns->crypto.rx.ckm->aead_ctx); - } - conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.rx.hp_ctx); - - if (conn->hs_pktns->crypto.tx.ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->hs_pktns->crypto.tx.ckm->aead_ctx); - } - conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.tx.hp_ctx); - } - if (conn->in_pktns) { - if (conn->in_pktns->crypto.rx.ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->in_pktns->crypto.rx.ckm->aead_ctx); - } - conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.rx.hp_ctx); - - if (conn->in_pktns->crypto.tx.ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->in_pktns->crypto.tx.ckm->aead_ctx); - } - conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.tx.hp_ctx); - } - - conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx); - - ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base); - ngtcp2_mem_free(conn->mem, conn->local.settings.token.base); - - ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); - ngtcp2_crypto_km_del(conn->crypto.key_update.new_rx_ckm, conn->mem); - ngtcp2_crypto_km_del(conn->crypto.key_update.new_tx_ckm, conn->mem); - ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); - - pktns_free(&conn->pktns, conn->mem); - pktns_del(conn->hs_pktns, conn->mem); - pktns_del(conn->in_pktns, conn->mem); - - cc_del(&conn->cc, conn->cc_algo, conn->mem); - - ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin); - - ngtcp2_ringbuf_free(&conn->rx.path_challenge); - - ngtcp2_pv_del(conn->pv); - - ngtcp2_idtr_free(&conn->remote.uni.idtr); - ngtcp2_idtr_free(&conn->remote.bidi.idtr); - ngtcp2_mem_free(conn->mem, conn->tx.ack); - ngtcp2_pq_free(&conn->tx.strmq); - ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn->mem); - ngtcp2_map_free(&conn->strms); - - ngtcp2_pq_free(&conn->scid.used); - delete_scid(&conn->scid.set, conn->mem); - ngtcp2_ksl_free(&conn->scid.set); - ngtcp2_gaptr_free(&conn->dcid.seqgap); - ngtcp2_ringbuf_free(&conn->dcid.retired); - ngtcp2_ringbuf_free(&conn->dcid.unused); - - ngtcp2_mem_free(conn->mem, conn); -} - -/* - * conn_ensure_ack_blks makes sure that conn->tx.ack->ack.blks can - * contain at least |n| additional ngtcp2_ack_blk. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_ensure_ack_blks(ngtcp2_conn *conn, size_t n) { - ngtcp2_frame *fr; - size_t max = conn->tx.max_ack_blks; - - if (n <= max) { - return 0; - } - - max *= 2; - - assert(max >= n); - - fr = ngtcp2_mem_realloc(conn->mem, conn->tx.ack, - sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_blk) * max); - if (fr == NULL) { - return NGTCP2_ERR_NOMEM; - } - - conn->tx.ack = fr; - conn->tx.max_ack_blks = max; - - return 0; -} - -/* - * conn_compute_ack_delay computes ACK delay for outgoing protected - * ACK. - */ -static ngtcp2_duration conn_compute_ack_delay(ngtcp2_conn *conn) { - return ngtcp2_min(conn->local.settings.transport_params.max_ack_delay, - conn->cstat.smoothed_rtt / 8); -} - -/* - * conn_create_ack_frame creates ACK frame, and assigns its pointer to - * |*pfr| if there are any received packets to acknowledge. If there - * are no packets to acknowledge, this function returns 0, and |*pfr| - * is untouched. The caller is advised to set |*pfr| to NULL before - * calling this function, and check it after this function returns. - * If |nodelay| is nonzero, delayed ACK timer is ignored. - * - * The memory for ACK frame is dynamically allocated by this function. - * A caller is responsible to free it. - * - * Call ngtcp2_acktr_commit_ack after a created ACK frame is - * successfully serialized into a packet. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, - ngtcp2_pktns *pktns, uint8_t type, - ngtcp2_tstamp ts, ngtcp2_duration ack_delay, - uint64_t ack_delay_exponent) { - /* TODO Measure an actual size of ACK blocks to find the best - default value. */ - const size_t initial_max_ack_blks = 8; - int64_t last_pkt_num; - ngtcp2_acktr *acktr = &pktns->acktr; - ngtcp2_ack_blk *blk; - ngtcp2_ksl_it it; - ngtcp2_acktr_entry *rpkt; - ngtcp2_ack *ack; - size_t blk_idx; - ngtcp2_tstamp largest_ack_ts; - int rv; - - if (acktr->flags & NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK) { - ack_delay = 0; - } - - if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) { - return 0; - } - - it = ngtcp2_acktr_get(acktr); - if (ngtcp2_ksl_it_end(&it)) { - ngtcp2_acktr_commit_ack(acktr); - return 0; - } - - if (conn->tx.ack == NULL) { - conn->tx.ack = ngtcp2_mem_malloc( - conn->mem, - sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_blk) * initial_max_ack_blks); - if (conn->tx.ack == NULL) { - return NGTCP2_ERR_NOMEM; - } - conn->tx.max_ack_blks = initial_max_ack_blks; - } - - ack = &conn->tx.ack->ack; - - ack->type = NGTCP2_FRAME_ACK; - ack->num_blks = 0; - - rpkt = ngtcp2_ksl_it_get(&it); - - if (rpkt->pkt_num == pktns->rx.max_pkt_num) { - last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); - largest_ack_ts = rpkt->tstamp; - ack->largest_ack = rpkt->pkt_num; - ack->first_ack_blklen = rpkt->len - 1; - - ngtcp2_ksl_it_next(&it); - } else { - assert(rpkt->pkt_num < pktns->rx.max_pkt_num); - - last_pkt_num = pktns->rx.max_pkt_num; - largest_ack_ts = pktns->rx.max_pkt_ts; - ack->largest_ack = pktns->rx.max_pkt_num; - ack->first_ack_blklen = 0; - } - - if (type == NGTCP2_PKT_SHORT) { - ack->ack_delay_unscaled = ts - largest_ack_ts; - ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / - (1UL << ack_delay_exponent); - } else { - ack->ack_delay_unscaled = 0; - ack->ack_delay = 0; - } - - for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - if (ack->num_blks == NGTCP2_MAX_ACK_BLKS) { - break; - } - - rpkt = ngtcp2_ksl_it_get(&it); - - blk_idx = ack->num_blks++; - rv = conn_ensure_ack_blks(conn, ack->num_blks); - if (rv != 0) { - return rv; - } - ack = &conn->tx.ack->ack; - blk = &ack->blks[blk_idx]; - blk->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2); - blk->blklen = rpkt->len - 1; - - last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); - } - - /* TODO Just remove entries which cannot fit into a single ACK frame - for now. */ - if (!ngtcp2_ksl_it_end(&it)) { - ngtcp2_acktr_forget(acktr, ngtcp2_ksl_it_get(&it)); - } - - *pfr = conn->tx.ack; - - return 0; -} - -/* - * conn_ppe_write_frame writes |fr| to |ppe|. If |hd_logged| is not - * NULL and |*hd_logged| is zero, packet header is logged, and 1 is - * assigned to |*hd_logged|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer is too small. - */ -static int conn_ppe_write_frame_hd_log(ngtcp2_conn *conn, ngtcp2_ppe *ppe, - int *hd_logged, const ngtcp2_pkt_hd *hd, - ngtcp2_frame *fr) { - int rv; - - rv = ngtcp2_ppe_encode_frame(ppe, fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return rv; - } - - if (hd_logged && !*hd_logged) { - *hd_logged = 1; - ngtcp2_log_tx_pkt_hd(&conn->log, hd); - ngtcp2_qlog_pkt_sent_start(&conn->qlog, hd); - } - - ngtcp2_log_tx_fr(&conn->log, hd, fr); - ngtcp2_qlog_write_frame(&conn->qlog, fr); - - return 0; -} - -/* - * conn_ppe_write_frame writes |fr| to |ppe|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer is too small. - */ -static int conn_ppe_write_frame(ngtcp2_conn *conn, ngtcp2_ppe *ppe, - const ngtcp2_pkt_hd *hd, ngtcp2_frame *fr) { - return conn_ppe_write_frame_hd_log(conn, ppe, NULL, hd, fr); -} - -/* - * conn_on_pkt_sent is called when new non-ACK-only packet is sent. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_rtb *rtb, - ngtcp2_rtb_entry *ent) { - int rv; - - /* This function implements OnPacketSent, but it handles only - non-ACK-only packet. */ - rv = ngtcp2_rtb_add(rtb, ent, &conn->cstat); - if (rv != 0) { - return rv; - } - - if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { - conn->cstat.last_tx_pkt_ts[rtb->pktns_id] = ent->ts; - } - - ngtcp2_conn_set_loss_detection_timer(conn, ent->ts); - - return 0; -} - -/* - * pktns_select_pkt_numlen selects shortest packet number encoding for - * the next packet number based on the largest acknowledged packet - * number. It returns the number of bytes to encode the packet - * number. - */ -static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { - int64_t pkt_num = pktns->tx.last_pkt_num + 1; - ngtcp2_rtb *rtb = &pktns->rtb; - int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num; - - if (NGTCP2_MAX_PKT_NUM / 2 <= pkt_num) { - return 4; - } - - n = n * 2 + 1; - - if (n > 0xffffff) { - return 4; - } - if (n > 0xffff) { - return 3; - } - if (n > 0xff) { - return 2; - } - return 1; -} - -/* - * conn_cwnd_is_zero returns nonzero if the number of bytes the local - * endpoint can sent at this time is zero. - */ -static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) { - uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; - uint64_t cwnd = - conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) - ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) - : conn->cstat.cwnd; - - return bytes_in_flight >= cwnd; -} - -/* - * conn_retry_early_payloadlen returns the estimated wire length of - * the first STREAM frame of 0-RTT packet which should be - * retransmitted due to Retry frame - */ -static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { - ngtcp2_frame_chain *frc; - ngtcp2_strm *strm; - - for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { - strm = ngtcp2_conn_tx_strmq_top(conn); - if (ngtcp2_strm_streamfrq_empty(strm)) { - ngtcp2_conn_tx_strmq_pop(conn); - continue; - } - - frc = ngtcp2_strm_streamfrq_top(strm); - return ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + - NGTCP2_STREAM_OVERHEAD; - } - - return 0; -} - -static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { - ngtcp2_frame_chain *frc; - ngtcp2_ksl_it it; - - for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, conn->mem); - } - ngtcp2_ksl_clear(&pktns->crypto.tx.frq); -} - -/* - * conn_cryptofrq_unacked_offset returns the CRYPTO frame offset by - * taking into account acknowledged offset. If there is no data to - * send, this function returns (uint64_t)-1. - */ -static uint64_t conn_cryptofrq_unacked_offset(ngtcp2_conn *conn, - ngtcp2_pktns *pktns) { - ngtcp2_frame_chain *frc; - ngtcp2_crypto *fr; - ngtcp2_range gap; - ngtcp2_rtb *rtb = &pktns->rtb; - ngtcp2_ksl_it it; - size_t datalen; - - (void)conn; - - for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - frc = ngtcp2_ksl_it_get(&it); - fr = &frc->fr.crypto; - - gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->offset); - - datalen = ngtcp2_vec_len(fr->data, fr->datacnt); - - if (gap.begin <= fr->offset) { - return fr->offset; - } - if (gap.begin < fr->offset + datalen) { - return gap.begin; - } - } - - return (uint64_t)-1; -} - -static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, - ngtcp2_frame_chain **pfrc) { - ngtcp2_frame_chain *frc, *nfrc; - ngtcp2_crypto *fr, *nfr; - uint64_t offset, end_offset; - size_t idx, end_idx; - uint64_t base_offset, end_base_offset; - ngtcp2_range gap; - ngtcp2_rtb *rtb = &pktns->rtb; - ngtcp2_vec *v; - int rv; - ngtcp2_ksl_it it; - - *pfrc = NULL; - - for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);) { - frc = ngtcp2_ksl_it_get(&it); - fr = &frc->fr.crypto; - - ngtcp2_ksl_remove(&pktns->crypto.tx.frq, &it, &fr->offset); - - idx = 0; - offset = fr->offset; - base_offset = 0; - - gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, offset); - if (gap.begin < offset) { - gap.begin = offset; - } - - for (; idx < fr->datacnt && offset < gap.begin; ++idx) { - v = &fr->data[idx]; - if (offset + v->len > gap.begin) { - base_offset = gap.begin - offset; - break; - } - - offset += v->len; - } - - if (idx == fr->datacnt) { - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - - assert(gap.begin == offset + base_offset); - - end_idx = idx; - end_offset = offset; - end_base_offset = 0; - - for (; end_idx < fr->datacnt; ++end_idx) { - v = &fr->data[end_idx]; - if (end_offset + v->len > gap.end) { - end_base_offset = gap.end - end_offset; - break; - } - - end_offset += v->len; - } - - if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) { - *pfrc = frc; - return 0; - } - - if (fr->datacnt == end_idx) { - memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); - - assert(fr->data[0].len > base_offset); - - fr->offset = offset + base_offset; - fr->datacnt = end_idx - idx; - fr->data[0].base += base_offset; - fr->data[0].len -= (size_t)base_offset; - - *pfrc = frc; - return 0; - } - - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx, - conn->mem); - if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - nfr = &nfrc->fr.crypto; - nfr->type = NGTCP2_FRAME_CRYPTO; - memcpy(nfr->data, fr->data + end_idx, - sizeof(nfr->data[0]) * (fr->datacnt - end_idx)); - - assert(nfr->data[0].len > end_base_offset); - - nfr->offset = end_offset + end_base_offset; - nfr->datacnt = fr->datacnt - end_idx; - nfr->data[0].base += end_base_offset; - nfr->data[0].len -= (size_t)end_base_offset; - - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - if (end_base_offset) { - ++end_idx; - } - - memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); - - assert(fr->data[0].len > base_offset); - - fr->offset = offset + base_offset; - fr->datacnt = end_idx - idx; - if (end_base_offset) { - assert(fr->data[fr->datacnt - 1].len > end_base_offset); - fr->data[fr->datacnt - 1].len = (size_t)end_base_offset; - } - fr->data[0].base += base_offset; - fr->data[0].len -= (size_t)base_offset; - - *pfrc = frc; - return 0; - } - - return 0; -} -static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, - ngtcp2_pktns *pktns, size_t left) { - ngtcp2_crypto *fr, *nfr; - ngtcp2_frame_chain *frc, *nfrc; - int rv; - size_t nmerged; - size_t datalen; - ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT]; - ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT]; - size_t acnt, bcnt; - ngtcp2_ksl_it it; - - rv = conn_cryptofrq_unacked_pop(conn, pktns, &frc); - if (rv != 0) { - return rv; - } - if (frc == NULL) { - *pfrc = NULL; - return 0; - } - - fr = &frc->fr.crypto; - datalen = ngtcp2_vec_len(fr->data, fr->datacnt); - - if (datalen > left) { - ngtcp2_vec_copy(a, fr->data, fr->datacnt); - acnt = fr->datacnt; - - bcnt = 0; - ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_CRYPTO_DATACNT); - - assert(acnt > 0); - assert(bcnt > 0); - - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - nfr = &nfrc->fr.crypto; - nfr->type = NGTCP2_FRAME_CRYPTO; - nfr->offset = fr->offset + left; - nfr->datacnt = bcnt; - ngtcp2_vec_copy(nfr->data, b, bcnt); - - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - nfr = &nfrc->fr.crypto; - *nfr = *fr; - nfr->datacnt = acnt; - ngtcp2_vec_copy(nfr->data, a, acnt); - - ngtcp2_frame_chain_del(frc, conn->mem); - - *pfrc = nfrc; - - return 0; - } - - left -= datalen; - - ngtcp2_vec_copy(a, fr->data, fr->datacnt); - acnt = fr->datacnt; - - for (; left && ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { - it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); - nfrc = ngtcp2_ksl_it_get(&it); - nfr = &nfrc->fr.crypto; - - if (nfr->offset != fr->offset + datalen) { - assert(fr->offset + datalen < nfr->offset); - break; - } - - rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - if (nfrc == NULL) { - break; - } - - nfr = &nfrc->fr.crypto; - - nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left, - NGTCP2_MAX_CRYPTO_DATACNT); - if (nmerged == 0) { - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - break; - } - - datalen += nmerged; - left -= nmerged; - - if (nfr->datacnt == 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); - continue; - } - - nfr->offset += nmerged; - - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - break; - } - - if (acnt == fr->datacnt) { - assert(acnt > 0); - fr->data[acnt - 1] = a[acnt - 1]; - - *pfrc = frc; - return 0; - } - - assert(acnt > fr->datacnt); - - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); - if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - nfr = &nfrc->fr.crypto; - *nfr = *fr; - nfr->datacnt = acnt; - ngtcp2_vec_copy(nfr->data, a, acnt); - - ngtcp2_frame_chain_del(frc, conn->mem); - - *pfrc = nfrc; - - return 0; -} - -/* - * conn_verify_dcid verifies that destination connection ID in |hd| is - * valid for the connection. If it is successfully verified and the - * remote endpoint uses new DCID in the packet, nonzero value is - * assigned to |*pnew_cid_used| if it is not NULL. Otherwise 0 is - * assigned to it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_INVALID_ARGUMENT - * |dcid| is not known to the local endpoint. - */ -static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used, - const ngtcp2_pkt_hd *hd) { - ngtcp2_ksl_it it; - ngtcp2_scid *scid; - int rv; - - it = ngtcp2_ksl_lower_bound(&conn->scid.set, &hd->dcid); - if (ngtcp2_ksl_it_end(&it)) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - scid = ngtcp2_ksl_it_get(&it); - if (!ngtcp2_cid_eq(&scid->cid, &hd->dcid)) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - if (!(scid->flags & NGTCP2_SCID_FLAG_USED)) { - scid->flags |= NGTCP2_SCID_FLAG_USED; - - if (scid->pe.index == NGTCP2_PQ_BAD_INDEX) { - rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe); - if (rv != 0) { - return rv; - } - } - - if (pnew_cid_used) { - *pnew_cid_used = 1; - } - } else if (pnew_cid_used) { - *pnew_cid_used = 0; - } - - return 0; -} - -/* - * conn_should_pad_pkt returns nonzero if the packet should be padded. - * |type| is the type of packet. |left| is the space left in packet - * buffer. |early_datalen| is the number of bytes which will be sent - * in the next, coalesced 0-RTT packet. - */ -static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, - size_t early_datalen) { - size_t min_payloadlen; - - if (conn->server) { - return 0; - } - - if (type == NGTCP2_PKT_HANDSHAKE) { - return conn->in_pktns != NULL; - } - - if (conn->hs_pktns->crypto.tx.ckm && - (conn->hs_pktns->rtb.probe_pkt_left || - ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || - !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { - /* If we have something to send in Handshake packet, then add - PADDING in Handshake packet. */ - min_payloadlen = 128; - } else if (!conn->early.ckm || early_datalen == 0) { - return 1; - } else { - /* If we have something to send in 0RTT packet, then add PADDING - in 0RTT packet. */ - min_payloadlen = ngtcp2_min(early_datalen, 128); - } - - return left < - /* TODO Assuming that pkt_num is encoded in 1 byte. */ - NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen + - conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ + - min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD; -} - -static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - conn->idle_ts = ts; - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; -} - -static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - conn->idle_ts = ts; - conn->flags |= NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; -} - -/* - * conn_write_handshake_pkt writes handshake packet in the buffer - * pointed by |dest| whose length is |destlen|. |type| specifies long - * packet type. It should be either NGTCP2_PKT_INITIAL or - * NGTCP2_PKT_HANDSHAKE_PKT. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, uint8_t type, - size_t early_datalen, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_ppe ppe; - ngtcp2_pkt_hd hd; - ngtcp2_frame_chain *frq = NULL, **pfrc = &frq; - ngtcp2_frame_chain *nfrc; - ngtcp2_frame *ackfr = NULL, lfr; - ngtcp2_ssize spktlen; - ngtcp2_crypto_cc cc; - ngtcp2_rtb_entry *rtbent; - ngtcp2_pktns *pktns; - size_t left; - uint8_t rtb_entry_flags = NGTCP2_RTB_FLAG_NONE; - int pkt_empty = 1; - int padded = 0; - int hd_logged = 0; - uint64_t crypto_offset; - ngtcp2_ssize num_reclaimed; - - switch (type) { - case NGTCP2_PKT_INITIAL: - if (!conn->in_pktns) { - return 0; - } - assert(conn->in_pktns->crypto.tx.ckm); - pktns = conn->in_pktns; - cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; - break; - case NGTCP2_PKT_HANDSHAKE: - if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { - return 0; - } - pktns = conn->hs_pktns; - cc.aead_overhead = conn->crypto.aead_overhead; - break; - default: - assert(0); - } - - cc.aead = pktns->crypto.ctx.aead; - cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; - cc.encrypt = conn->callbacks.encrypt; - cc.hp_mask = conn->callbacks.hp_mask; - - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, type, - &conn->dcid.current.cid, &conn->oscid, - pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); - - if (!conn->server && type == NGTCP2_PKT_INITIAL && - conn->local.settings.token.len) { - hd.token = conn->local.settings.token; - } - - ngtcp2_ppe_init(&ppe, dest, destlen, &cc); - - rv = ngtcp2_ppe_encode_hd(&ppe, &hd); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return 0; - } - - if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { - return 0; - } - - rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts, - /* ack_delay = */ 0, - NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); - if (rv != 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); - return rv; - } - - if (ackfr) { - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - ngtcp2_acktr_commit_ack(&pktns->acktr); - ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack); - pkt_empty = 0; - } - } - -build_pkt: - for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { - left = ngtcp2_ppe_left(&ppe); - - crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns); - if (crypto_offset == (size_t)-1) { - conn_cryptofrq_clear(conn, pktns); - break; - } - - left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); - if (left == (size_t)-1) { - break; - } - - rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); - return rv; - } - - if (nfrc == NULL) { - break; - } - - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr); - if (rv != 0) { - assert(0); - } - - *pfrc = nfrc; - pfrc = &(*pfrc)->next; - - pkt_empty = 0; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - } - - if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && - pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); - if (num_reclaimed < 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); - return rv; - } - if (num_reclaimed) { - goto build_pkt; - } - /* We had pktns->rtb.num_retransmittable > 0 but the contents of - 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. */ - if (pktns->rtb.num_retransmittable == 0 && - (conn->server || - (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | - NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { - pktns->rtb.probe_pkt_left = 0; - ngtcp2_conn_set_loss_detection_timer(conn, ts); - } - } - - /* Don't send any PING frame if client Initial has not been - acknowledged yet. */ - if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && - pktns->rtb.probe_pkt_left && - (type != NGTCP2_PKT_INITIAL || - ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { - lfr.type = NGTCP2_FRAME_PING; - - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); - if (rv != 0) { - assert(rv == NGTCP2_ERR_NOBUF); - } else { - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_PROBE; - pkt_empty = 0; - } - } - - if (!pkt_empty) { - if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { - /* The intention of smaller limit is get more chance to measure - RTT samples in early phase. */ - if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) { - lfr.type = NGTCP2_FRAME_PING; - - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); - if (rv != 0) { - assert(rv == NGTCP2_ERR_NOBUF); - } else { - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; - pktns->tx.num_non_ack_pkt = 0; - } - } else { - ++pktns->tx.num_non_ack_pkt; - } - } else { - pktns->tx.num_non_ack_pkt = 0; - } - } - - if (pkt_empty) { - return 0; - } - - /* If we cannot write another packet, then we need to add padding to - Initial here. */ - if (conn_should_pad_pkt(conn, type, ngtcp2_ppe_left(&ppe), early_datalen)) { - lfr.type = NGTCP2_FRAME_PADDING; - lfr.padding.len = ngtcp2_ppe_padding(&ppe); - } else { - lfr.type = NGTCP2_FRAME_PADDING; - lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); - } - - if (lfr.padding.len) { - padded = 1; - ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); - ngtcp2_qlog_write_frame(&conn->qlog, &lfr); - } - - spktlen = ngtcp2_ppe_final(&ppe, NULL); - if (spktlen < 0) { - assert(ngtcp2_err_is_fatal((int)spktlen)); - ngtcp2_frame_chain_list_del(frq, conn->mem); - return spktlen; - } - - ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen); - - if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen, - rtb_entry_flags, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); - return rv; - } - - rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); - if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); - return rv; - } - - if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && - (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { - conn_restart_timer_on_write(conn, ts); - } - } - - if (pktns->rtb.probe_pkt_left && - (rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { - --pktns->rtb.probe_pkt_left; - } - - ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - - ++pktns->tx.last_pkt_num; - - return spktlen; -} - -/* - * conn_write_ack_pkt writes QUIC packet for type |type| which only - * includes ACK frame in the buffer pointed by |dest| whose length is - * |destlen|. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, uint8_t type, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_frame *ackfr; - ngtcp2_pktns *pktns; - ngtcp2_duration ack_delay; - uint64_t ack_delay_exponent; - - assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); - - switch (type) { - case NGTCP2_PKT_INITIAL: - assert(conn->server); - pktns = conn->in_pktns; - ack_delay = 0; - ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; - break; - case NGTCP2_PKT_HANDSHAKE: - pktns = conn->hs_pktns; - ack_delay = 0; - ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; - break; - case NGTCP2_PKT_SHORT: - pktns = &conn->pktns; - ack_delay = conn_compute_ack_delay(conn); - ack_delay_exponent = - conn->local.settings.transport_params.ack_delay_exponent; - break; - default: - assert(0); - } - - if (!pktns->crypto.tx.ckm) { - return 0; - } - - ackfr = NULL; - rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts, ack_delay, - ack_delay_exponent); - if (rv != 0) { - return rv; - } - - if (!ackfr) { - return 0; - } - - return ngtcp2_conn_write_single_frame_pkt(conn, dest, destlen, type, - &conn->dcid.current.cid, ackfr, - NGTCP2_RTB_FLAG_NONE, ts); -} - -static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns **ppktns, - ngtcp2_tstamp ts) { - ngtcp2_pktns *pktns = *ppktns; - uint64_t bytes_in_flight; - - bytes_in_flight = pktns->rtb.cc_bytes_in_flight; - - assert(conn->cstat.bytes_in_flight >= bytes_in_flight); - - conn->cstat.bytes_in_flight -= bytes_in_flight; - conn->cstat.pto_count = 0; - conn->cstat.last_tx_pkt_ts[pktns->rtb.pktns_id] = UINT64_MAX; - conn->cstat.loss_time[pktns->rtb.pktns_id] = UINT64_MAX; - - conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx); - conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); - conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); - conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx); - - pktns_del(pktns, conn->mem); - *ppktns = NULL; - - ngtcp2_conn_set_loss_detection_timer(conn, ts); -} - -/* - * conn_discard_initial_state discards state for Initial packet number - * space. - */ -static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - if (!conn->in_pktns) { - return; - } - - conn_discard_pktns(conn, &conn->in_pktns, ts); -} - -/* - * conn_discard_handshake_state discards state for Handshake packet - * number space. - */ -static void conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - if (!conn->hs_pktns) { - return; - } - - conn_discard_pktns(conn, &conn->hs_pktns, ts); -} - -/* - * conn_write_handshake_ack_pkts writes packets which contain ACK - * frame only. This function writes at most 2 packets for each - * Initial and Handshake packet. - */ -static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn, - uint8_t *dest, size_t destlen, - ngtcp2_tstamp ts) { - ngtcp2_ssize res = 0, nwrite = 0; - - /* In the most cases, client sends ACK in conn_write_handshake_pkt. - This function is only called when it is CWND limited. It is not - required for client to send ACK for server Initial. This is - because once it gets server Initial, it gets Handshake tx key and - discards Initial key. The only good reason to send ACK is give - server RTT measurement early. */ - if (conn->server && conn->in_pktns) { - nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, ts); - if (nwrite < 0) { - assert(nwrite != NGTCP2_ERR_NOBUF); - return nwrite; - } - - res += nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - } - - if (conn->hs_pktns->crypto.tx.ckm) { - nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_HANDSHAKE, ts); - if (nwrite < 0) { - assert(nwrite != NGTCP2_ERR_NOBUF); - return nwrite; - } - - res += nwrite; - - if (!conn->server && nwrite) { - conn_discard_initial_state(conn, ts); - } - } - - return res; -} - -/* - * conn_write_client_initial writes Initial packet in the buffer - * pointed by |dest| whose length is |destlen|. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, - size_t early_datalen, - ngtcp2_tstamp ts) { - int rv; - - assert(conn->callbacks.client_initial); - - rv = conn->callbacks.client_initial(conn, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, - early_datalen, ts); -} - -/* - * conn_write_handshake_pkts writes Initial and Handshake packets in - * the buffer pointed by |dest| whose length is |destlen|. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, - size_t early_datalen, - ngtcp2_tstamp ts) { - ngtcp2_ssize nwrite; - ngtcp2_ssize res = 0; - - if (!conn->server && conn->hs_pktns->crypto.tx.ckm && - !ngtcp2_acktr_empty(&conn->hs_pktns->acktr)) { - /* Discard Initial state here so that Handshake packet is not - padded. */ - conn_discard_initial_state(conn, ts); - } else { - nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, - early_datalen, ts); - if (nwrite < 0) { - assert(nwrite != NGTCP2_ERR_NOBUF); - return nwrite; - } - - res += nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - } - - nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_HANDSHAKE, - 0, ts); - if (nwrite < 0) { - assert(nwrite != NGTCP2_ERR_NOBUF); - return nwrite; - } - - res += nwrite; - - if (!conn->server && conn->hs_pktns->crypto.tx.ckm && nwrite) { - /* We don't need to send further Initial packet if we have - Handshake key and sent something with it. So discard initial - state here. */ - conn_discard_initial_state(conn, ts); - } - - return res; -} - -/* - * conn_initial_stream_rx_offset returns the initial maximum offset of - * data for a stream denoted by |stream_id|. - */ -static uint64_t conn_initial_stream_rx_offset(ngtcp2_conn *conn, - int64_t stream_id) { - int local_stream = conn_local_stream(conn, stream_id); - - if (bidi_stream(stream_id)) { - if (local_stream) { - return conn->local.settings.transport_params - .initial_max_stream_data_bidi_local; - } - return conn->local.settings.transport_params - .initial_max_stream_data_bidi_remote; - } - - if (local_stream) { - return 0; - } - return conn->local.settings.transport_params.initial_max_stream_data_uni; -} - -/* - * conn_should_send_max_stream_data returns nonzero if MAX_STREAM_DATA - * frame should be send for |strm|. - */ -static int conn_should_send_max_stream_data(ngtcp2_conn *conn, - ngtcp2_strm *strm) { - uint64_t win = conn_initial_stream_rx_offset(conn, strm->stream_id); - uint64_t inc = strm->rx.unsent_max_offset - strm->rx.max_offset; - ngtcp2_conn_stat *cstat = &conn->cstat; - - return win < 2 * inc || - (cstat->min_rtt != UINT64_MAX && - 2 * cstat->recv_rate_sec * cstat->min_rtt / NGTCP2_SECONDS > - win - inc); -} - -/* - * conn_should_send_max_data returns nonzero if MAX_DATA frame should - * be sent. - */ -static int conn_should_send_max_data(ngtcp2_conn *conn) { - uint64_t inc = conn->rx.unsent_max_offset - conn->rx.max_offset; - ngtcp2_conn_stat *cstat = &conn->cstat; - - return conn->local.settings.transport_params.initial_max_data < 2 * inc || - (cstat->min_rtt != UINT64_MAX && - 2 * cstat->recv_rate_sec * cstat->min_rtt / NGTCP2_SECONDS > - conn->local.settings.transport_params.initial_max_data - inc); -} - -/* - * conn_required_num_new_connection_id returns the number of - * additional connection ID the local endpoint has to provide to the - * remote endpoint. - */ -static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) { - uint64_t n; - size_t len = ngtcp2_ksl_len(&conn->scid.set); - - if (len >= NGTCP2_MAX_SCID_POOL_SIZE) { - return 0; - } - - /* len includes retired CID. We don't provide extra CID if doing so - excceds NGTCP2_MAX_SCID_POOL_SIZE. */ - - n = conn->remote.transport_params.active_connection_id_limit + - conn->scid.num_retired; - - return (size_t)ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len; -} - -/* - * conn_enqueue_new_connection_id generates additional connection IDs - * and prepares to send them to the remote endpoint. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { - size_t i, need = conn_required_num_new_connection_id(conn); - size_t cidlen = conn->oscid.datalen; - ngtcp2_cid cid; - uint64_t seq; - int rv; - uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; - ngtcp2_frame_chain *nfrc; - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_scid *scid; - ngtcp2_ksl_it it; - - for (i = 0; i < need; ++i) { - rv = conn_call_get_new_connection_id(conn, &cid, token, cidlen); - if (rv != 0) { - return rv; - } - - if (cid.datalen != cidlen) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - /* Assert uniqueness */ - it = ngtcp2_ksl_lower_bound(&conn->scid.set, &cid); - if (!ngtcp2_ksl_it_end(&it) && - ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it), &cid)) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - seq = ++conn->scid.last_seq; - - scid = ngtcp2_mem_malloc(conn->mem, sizeof(*scid)); - if (scid == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_scid_init(scid, seq, &cid, token); - - rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid); - if (rv != 0) { - ngtcp2_mem_free(conn->mem, scid); - return rv; - } - - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; - nfrc->fr.new_connection_id.seq = seq; - nfrc->fr.new_connection_id.retire_prior_to = 0; - nfrc->fr.new_connection_id.cid = cid; - memcpy(nfrc->fr.new_connection_id.stateless_reset_token, token, - sizeof(token)); - nfrc->next = pktns->tx.frq; - pktns->tx.frq = nfrc; - } - - return 0; -} - -/* - * conn_compute_pto computes the current PTO. - */ -static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn, - ngtcp2_pktns *pktns) { - ngtcp2_conn_stat *cstat = &conn->cstat; - ngtcp2_duration var = ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY); - ngtcp2_duration max_ack_delay = - pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APP - ? conn->remote.transport_params.max_ack_delay - : 0; - return cstat->smoothed_rtt + var + max_ack_delay; -} - -/* - * conn_remove_retired_connection_id removes the already retired - * connection ID. It waits PTO before actually removing a connection - * ID after it receives RETIRE_CONNECTION_ID from peer to catch - * reordered packets. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_remove_retired_connection_id(ngtcp2_conn *conn, - ngtcp2_tstamp ts) { - ngtcp2_duration timeout = conn_compute_pto(conn, &conn->pktns); - ngtcp2_scid *scid; - ngtcp2_dcid *dcid; - int rv; - - for (; !ngtcp2_pq_empty(&conn->scid.used);) { - scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); - - if (scid->ts_retired == UINT64_MAX || scid->ts_retired + timeout >= ts) { - break; - } - - assert(scid->flags & NGTCP2_SCID_FLAG_RETIRED); - - rv = conn_call_remove_connection_id(conn, &scid->cid); - if (rv != 0) { - return rv; - } - - ngtcp2_ksl_remove(&conn->scid.set, NULL, &scid->cid); - ngtcp2_pq_pop(&conn->scid.used); - ngtcp2_mem_free(conn->mem, scid); - - assert(conn->scid.num_retired); - --conn->scid.num_retired; - } - - for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); - if (dcid->ts_retired + timeout >= ts) { - break; - } - - rv = conn_call_deactivate_dcid(conn, dcid); - if (rv != 0) { - return rv; - } - - ngtcp2_ringbuf_pop_front(&conn->dcid.retired); - } - - return 0; -} - -/* - * conn_min_short_pktlen returns the minimum length of Short packet - * this endpoint sends. - */ -static size_t conn_min_short_pktlen(ngtcp2_conn *conn) { - return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN; -} - -typedef enum { - NGTCP2_WRITE_PKT_FLAG_NONE = 0x00, - /* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet - should be padded */ - NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING = 0x01, - /* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come - and it should be encoded into the current packet. */ - NGTCP2_WRITE_PKT_FLAG_MORE = 0x02, -} ngtcp2_write_pkt_flag; - -/* - * conn_write_pkt writes a protected packet in the buffer pointed by - * |dest| whose length if |destlen|. |type| specifies the type of - * packet. It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT. - * - * This function can send new stream data. In order to send stream - * data, specify the underlying stream and parameters to - * |vmsg|->stream. If |vmsg|->stream.fin is set to nonzero, it - * signals that the given data is the final portion of the stream. - * |vmsg|->stream.data vector of length |vmsg|->stream.datacnt - * specifies stream data to send. The number of bytes sent to the - * stream is assigned to *|vmsg|->stream.pdatalen. If 0 length STREAM - * data is sent, 0 is assigned to it. The caller should initialize - * *|vmsg|->stream.pdatalen to -1. - * - * If |require_padding| is nonzero, padding bytes are added to occupy - * the remaining packet payload. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_STREAM_DATA_BLOCKED - * Stream data could not be written because of flow control. - */ -static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, ngtcp2_vmsg *vmsg, - uint8_t type, uint8_t flags, - ngtcp2_tstamp ts) { - int rv = 0; - ngtcp2_crypto_cc *cc = &conn->pkt.cc; - ngtcp2_ppe *ppe = &conn->pkt.ppe; - ngtcp2_pkt_hd *hd = &conn->pkt.hd; - ngtcp2_frame *ackfr = NULL, lfr; - ngtcp2_ssize nwrite; - ngtcp2_frame_chain **pfrc, *nfrc, *frc; - ngtcp2_rtb_entry *ent; - ngtcp2_strm *strm; - int pkt_empty = 1; - size_t ndatalen = 0; - int send_stream = 0; - int stream_blocked = 0; - ngtcp2_pktns *pktns = &conn->pktns; - size_t left; - size_t datalen = 0; - ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; - size_t datacnt; - uint8_t rtb_entry_flags = NGTCP2_RTB_FLAG_NONE; - int hd_logged = 0; - ngtcp2_path_challenge_entry *pcent; - uint8_t hd_flags; - int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; - int write_more = (flags & NGTCP2_WRITE_PKT_FLAG_MORE) != 0; - int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; - size_t min_pktlen = conn_min_short_pktlen(conn); - int padded = 0; - int credit_expanded = 0; - ngtcp2_cc_pkt cc_pkt; - uint64_t crypto_offset; - uint64_t stream_offset; - ngtcp2_ssize num_reclaimed; - int fin; - - /* Return 0 if destlen is less than minimum packet length which can - trigger Stateless Reset */ - if (destlen < min_pktlen) { - return 0; - } - - if (vmsg) { - switch (vmsg->type) { - case NGTCP2_VMSG_TYPE_STREAM: - datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); - ndatalen = conn_enforce_flow_control(conn, vmsg->stream.strm, datalen); - /* 0 length STREAM frame is allowed */ - if (ndatalen || datalen == 0) { - send_stream = 1; - } else { - stream_blocked = 1; - } - break; - default: - break; - } - } - - if (!ppe_pending) { - switch (type) { - case NGTCP2_PKT_SHORT: - hd_flags = - (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) - ? NGTCP2_PKT_FLAG_KEY_PHASE - : NGTCP2_PKT_FLAG_NONE; - cc->ckm = pktns->crypto.tx.ckm; - cc->hp_ctx = pktns->crypto.tx.hp_ctx; - - /* transport parameter is only valid after handshake completion - which means we don't know how many connection ID that remote - peer can accept before handshake completion. */ - if (conn->oscid.datalen && - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - rv = conn_enqueue_new_connection_id(conn); - if (rv != 0) { - return rv; - } - } - - break; - case NGTCP2_PKT_0RTT: - assert(!conn->server); - if (!conn->early.ckm) { - return 0; - } - hd_flags = NGTCP2_PKT_FLAG_LONG_FORM; - cc->ckm = conn->early.ckm; - cc->hp_ctx = conn->early.hp_ctx; - break; - default: - /* Unreachable */ - assert(0); - } - - cc->aead = pktns->crypto.ctx.aead; - cc->hp = pktns->crypto.ctx.hp; - cc->aead_overhead = conn->crypto.aead_overhead; - cc->encrypt = conn->callbacks.encrypt; - cc->hp_mask = conn->callbacks.hp_mask; - - /* TODO Take into account stream frames */ - if ((pktns->tx.frq || send_stream || - ngtcp2_ringbuf_len(&conn->rx.path_challenge) || - conn_should_send_max_data(conn)) && - conn->rx.unsent_max_offset > conn->rx.max_offset) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - return rv; - } - nfrc->fr.type = NGTCP2_FRAME_MAX_DATA; - nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset; - nfrc->next = pktns->tx.frq; - pktns->tx.frq = nfrc; - - conn->rx.max_offset = conn->rx.unsent_max_offset; - credit_expanded = 1; - } - - ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, - &conn->oscid, pktns->tx.last_pkt_num + 1, - pktns_select_pkt_numlen(pktns), conn->version, 0); - - ngtcp2_ppe_init(ppe, dest, destlen, cc); - - rv = ngtcp2_ppe_encode_hd(ppe, hd); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return 0; - } - - if (!ngtcp2_ppe_ensure_hp_sample(ppe)) { - return 0; - } - - if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); - - lfr.type = NGTCP2_FRAME_PATH_RESPONSE; - memcpy(lfr.path_response.data, pcent->data, - sizeof(lfr.path_response.data)); - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); - - pkt_empty = 0; - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; - /* We don't retransmit PATH_RESPONSE. */ - } - } - - rv = conn_create_ack_frame( - conn, &ackfr, pktns, type, ts, conn_compute_ack_delay(conn), - conn->local.settings.transport_params.ack_delay_exponent); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - if (ackfr) { - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - ngtcp2_acktr_commit_ack(&pktns->acktr); - ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, - ackfr->ack.largest_ack); - pkt_empty = 0; - } - } - - build_pkt: - for (pfrc = &pktns->tx.frq; *pfrc;) { - if ((*pfrc)->binder && - ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - - switch ((*pfrc)->fr.type) { - case NGTCP2_FRAME_STOP_SENDING: - strm = - ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id); - if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_STREAM: - assert(0); - break; - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - if ((*pfrc)->fr.max_streams.max_streams < - conn->remote.bidi.max_streams) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_MAX_STREAMS_UNI: - if ((*pfrc)->fr.max_streams.max_streams < - conn->remote.uni.max_streams) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_MAX_STREAM_DATA: - strm = ngtcp2_conn_find_stream(conn, - (*pfrc)->fr.max_stream_data.stream_id); - if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) || - (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_MAX_DATA: - if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { - frc = *pfrc; - *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); - continue; - } - break; - case NGTCP2_FRAME_CRYPTO: - assert(0); - break; - } - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - break; - } - - pkt_empty = 0; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - pfrc = &(*pfrc)->next; - } - - if (rv != NGTCP2_ERR_NOBUF) { - for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { - left = ngtcp2_ppe_left(ppe); - - crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns); - if (crypto_offset == (size_t)-1) { - conn_cryptofrq_clear(conn, pktns); - break; - } - - left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); - - if (left == (size_t)-1) { - break; - } - - rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - if (nfrc == NULL) { - break; - } - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); - if (rv != 0) { - assert(0); - } - - *pfrc = nfrc; - pfrc = &(*pfrc)->next; - - pkt_empty = 0; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - } - } - - /* Write MAX_STREAM_ID after RESET_STREAM so that we can extend stream - ID space in one packet. */ - if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL && - conn->remote.bidi.unsent_max_streams > conn->remote.bidi.max_streams) { - rv = conn_call_extend_max_remote_streams_bidi( - conn, conn->remote.bidi.unsent_max_streams); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI; - nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams; - *pfrc = nfrc; - - conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams; - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - pkt_empty = 0; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - pfrc = &(*pfrc)->next; - } - } - - if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL) { - if (conn->remote.uni.unsent_max_streams > conn->remote.uni.max_streams) { - rv = conn_call_extend_max_remote_streams_uni( - conn, conn->remote.uni.unsent_max_streams); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI; - nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams; - *pfrc = nfrc; - - conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams; - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, - &(*pfrc)->fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - pkt_empty = 0; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - pfrc = &(*pfrc)->next; - } - } - } - - if (rv != NGTCP2_ERR_NOBUF) { - for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { - strm = ngtcp2_conn_tx_strmq_top(conn); - - if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && - strm->rx.max_offset < strm->rx.unsent_max_offset) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; - nfrc->fr.max_stream_data.stream_id = strm->stream_id; - nfrc->fr.max_stream_data.max_stream_data = strm->rx.unsent_max_offset; - ngtcp2_list_insert(nfrc, pfrc); - - rv = - conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - break; - } - - pkt_empty = 0; - credit_expanded = 1; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - pfrc = &(*pfrc)->next; - strm->rx.max_offset = strm->rx.unsent_max_offset; - } - - if (ngtcp2_strm_streamfrq_empty(strm)) { - ngtcp2_conn_tx_strmq_pop(conn); - continue; - } - - stream_offset = ngtcp2_strm_streamfrq_unacked_offset(strm); - if (stream_offset == (uint64_t)-1) { - ngtcp2_strm_streamfrq_clear(strm); - ngtcp2_conn_tx_strmq_pop(conn); - continue; - } - - left = ngtcp2_ppe_left(ppe); - - left = ngtcp2_pkt_stream_max_datalen(strm->stream_id, stream_offset, - left, left); - - if (left == (size_t)-1) { - break; - } - - rv = ngtcp2_strm_streamfrq_pop(strm, &nfrc, left); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - if (nfrc == NULL) { - /* TODO Why? */ - break; - } - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); - if (rv != 0) { - assert(0); - } - - *pfrc = nfrc; - pfrc = &(*pfrc)->next; - - pkt_empty = 0; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - - if (ngtcp2_strm_streamfrq_empty(strm)) { - ngtcp2_conn_tx_strmq_pop(conn); - continue; - } - - ngtcp2_conn_tx_strmq_pop(conn); - ++strm->cycle; - rv = ngtcp2_conn_tx_strmq_push(conn, strm); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - } - } - - /* Add ACK if MAX_DATA or MAX_STREAM_DATA frame is encoded to - decrease packet count. */ - if (ackfr == NULL && credit_expanded) { - rv = conn_create_ack_frame( - conn, &ackfr, pktns, type, ts, /* ack_delay = */ 0, - conn->local.settings.transport_params.ack_delay_exponent); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - if (ackfr) { - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - ngtcp2_acktr_commit_ack(&pktns->acktr); - ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, - ackfr->ack.largest_ack); - } - } - } - - if (rv != NGTCP2_ERR_NOBUF && !send_stream && - !(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && - pktns->rtb.num_retransmittable && pktns->tx.frq == NULL && - pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); - if (num_reclaimed < 0) { - return rv; - } - if (num_reclaimed) { - goto build_pkt; - } - - /* We had pktns->rtb.num_retransmittable > 0 but the contents of - those packets have been acknowledged (i.e., retransmission in - another packet). In this case, we don't have to send any - probe packet. */ - if (pktns->rtb.num_retransmittable == 0) { - pktns->rtb.probe_pkt_left = 0; - ngtcp2_conn_set_loss_detection_timer(conn, ts); - } - } - } else { - pfrc = conn->pkt.pfrc; - rtb_entry_flags |= conn->pkt.rtb_entry_flags; - pkt_empty = conn->pkt.pkt_empty; - hd_logged = conn->pkt.hd_logged; - } - - left = ngtcp2_ppe_left(ppe); - - if (rv != NGTCP2_ERR_NOBUF && send_stream && *pfrc == NULL && - (ndatalen = ngtcp2_pkt_stream_max_datalen( - vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, - left)) != (size_t)-1 && - (ndatalen || datalen == 0)) { - datacnt = ngtcp2_vec_copy_at_most( - data, &ndatalen, NGTCP2_MAX_STREAM_DATACNT, vmsg->stream.data, - vmsg->stream.datacnt, ndatalen); - - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - nfrc->fr.stream.type = NGTCP2_FRAME_STREAM; - nfrc->fr.stream.flags = 0; - nfrc->fr.stream.stream_id = vmsg->stream.strm->stream_id; - nfrc->fr.stream.offset = vmsg->stream.strm->tx.offset; - nfrc->fr.stream.datacnt = datacnt; - ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt); - - fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) && - ndatalen == datalen; - nfrc->fr.stream.fin = (uint8_t)fin; - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); - if (rv != 0) { - assert(0); - } - - *pfrc = nfrc; - pfrc = &(*pfrc)->next; - - pkt_empty = 0; - rtb_entry_flags |= - NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE; - - vmsg->stream.strm->tx.offset += ndatalen; - conn->tx.offset += ndatalen; - - if (fin) { - ngtcp2_strm_shutdown(vmsg->stream.strm, NGTCP2_STRM_FLAG_SHUT_WR); - } - - if (vmsg->stream.pdatalen) { - *vmsg->stream.pdatalen = (ngtcp2_ssize)ndatalen; - } - } else { - send_stream = 0; - } - - if (pkt_empty) { - assert(rv == 0 || NGTCP2_ERR_NOBUF == rv); - if (rv == 0 && stream_blocked && ngtcp2_conn_get_max_data_left(conn)) { - return NGTCP2_ERR_STREAM_DATA_BLOCKED; - } - - if (conn->pktns.rtb.probe_pkt_left == 0) { - return 0; - } - } else if (write_more) { - conn->pkt.pfrc = pfrc; - conn->pkt.pkt_empty = pkt_empty; - conn->pkt.rtb_entry_flags = rtb_entry_flags; - conn->pkt.hd_logged = hd_logged; - conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; - - if (send_stream) { - return NGTCP2_ERR_WRITE_MORE; - } - - if (ngtcp2_conn_get_max_data_left(conn) && stream_blocked) { - return NGTCP2_ERR_STREAM_DATA_BLOCKED; - } - } - - if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { - if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) { - lfr.type = NGTCP2_FRAME_PING; - - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); - if (rv != 0) { - assert(rv == NGTCP2_ERR_NOBUF); - /* TODO If buffer is too small, PING cannot be written if - packet is still empty. */ - } else { - rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; - pktns->tx.num_non_ack_pkt = 0; - } - } else { - ++pktns->tx.num_non_ack_pkt; - } - } else { - pktns->tx.num_non_ack_pkt = 0; - } - - /* TODO Push STREAM frame back to ngtcp2_strm if there is an error - before ngtcp2_rtb_entry is safely created and added. */ - lfr.type = NGTCP2_FRAME_PADDING; - if ((require_padding || - /* Making full sized packet will help GSO a bit */ - ngtcp2_ppe_left(ppe) < 10 || - (type == NGTCP2_PKT_0RTT && conn->state == NGTCP2_CS_CLIENT_INITIAL)) && - ngtcp2_ppe_left(ppe)) { - lfr.padding.len = ngtcp2_ppe_padding(ppe); - } else { - lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); - } - - if (lfr.padding.len) { - padded = 1; - ngtcp2_log_tx_fr(&conn->log, hd, &lfr); - ngtcp2_qlog_write_frame(&conn->qlog, &lfr); - } - - nwrite = ngtcp2_ppe_final(ppe, NULL); - if (nwrite < 0) { - assert(ngtcp2_err_is_fatal((int)nwrite)); - return nwrite; - } - - ++cc->ckm->use_count; - - ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite); - - /* TODO ack-eliciting vs needs-tracking */ - /* probe packet needs tracking but it does not need ACK, could be lost. */ - if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { - rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, - rtb_entry_flags, conn->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal((int)nwrite)); - return rv; - } - - if (*pfrc != pktns->tx.frq) { - ent->frc = pktns->tx.frq; - pktns->tx.frq = *pfrc; - *pfrc = NULL; - } - - if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && - pktns->rtb.num_ack_eliciting == 0 && conn->cc.event) { - conn->cc.event(&conn->cc, &conn->cstat, NGTCP2_CC_EVENT_TYPE_TX_START, - ts); - } - - rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_rtb_entry_del(ent, conn->mem); - return rv; - } - - if (rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { - if (conn->cc.on_pkt_sent) { - conn->cc.on_pkt_sent(&conn->cc, &conn->cstat, - ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, - (size_t)nwrite, - NGTCP2_PKTNS_ID_APP, ts)); - } - - if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { - conn_restart_timer_on_write(conn, ts); - } - } - } - - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING; - - if (pktns->rtb.probe_pkt_left && - (rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { - --pktns->rtb.probe_pkt_left; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", - nwrite); - } - - ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - - ++pktns->tx.last_pkt_num; - - return nwrite; -} - -ngtcp2_ssize -ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, uint8_t type, - const ngtcp2_cid *dcid, ngtcp2_frame *fr, - uint8_t rtb_flags, ngtcp2_tstamp ts) { - int rv; - ngtcp2_ppe ppe; - ngtcp2_pkt_hd hd; - ngtcp2_frame lfr; - ngtcp2_ssize nwrite; - ngtcp2_crypto_cc cc; - ngtcp2_pktns *pktns; - uint8_t flags; - ngtcp2_rtb_entry *rtbent; - int padded = 0; - - switch (type) { - case NGTCP2_PKT_INITIAL: - pktns = conn->in_pktns; - cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; - flags = NGTCP2_PKT_FLAG_LONG_FORM; - break; - case NGTCP2_PKT_HANDSHAKE: - pktns = conn->hs_pktns; - cc.aead_overhead = conn->crypto.aead_overhead; - flags = NGTCP2_PKT_FLAG_LONG_FORM; - break; - case NGTCP2_PKT_SHORT: - /* 0 means Short packet. */ - pktns = &conn->pktns; - cc.aead_overhead = conn->crypto.aead_overhead; - flags = (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) - ? NGTCP2_PKT_FLAG_KEY_PHASE - : NGTCP2_PKT_FLAG_NONE; - break; - default: - /* We don't support 0-RTT packet in this function. */ - assert(0); - } - - cc.aead = pktns->crypto.ctx.aead; - cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; - cc.encrypt = conn->callbacks.encrypt; - cc.hp_mask = conn->callbacks.hp_mask; - - ngtcp2_pkt_hd_init(&hd, flags, type, dcid, &conn->oscid, - pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); - - ngtcp2_ppe_init(&ppe, dest, destlen, &cc); - - rv = ngtcp2_ppe_encode_hd(&ppe, &hd); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return 0; - } - - if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { - return 0; - } - - ngtcp2_log_tx_pkt_hd(&conn->log, &hd); - ngtcp2_qlog_pkt_sent_start(&conn->qlog, &hd); - - rv = conn_ppe_write_frame(conn, &ppe, &hd, fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return 0; - } - - lfr.type = NGTCP2_FRAME_PADDING; - if (type == NGTCP2_PKT_SHORT) { - lfr.padding.len = - ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); - } else { - lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); - } - if (lfr.padding.len) { - padded = 1; - ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); - ngtcp2_qlog_write_frame(&conn->qlog, &lfr); - } - - nwrite = ngtcp2_ppe_final(&ppe, NULL); - if (nwrite < 0) { - return nwrite; - } - - ++cc.ckm->use_count; - - ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite); - - /* Do this when we are sure that there is no error. */ - if (fr->type == NGTCP2_FRAME_ACK) { - ngtcp2_acktr_commit_ack(&pktns->acktr); - ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack); - } - - if ((rtb_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags, - conn->mem); - if (rv != 0) { - return rv; - } - - rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); - if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); - return rv; - } - - if ((rtb_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && - (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { - conn_restart_timer_on_write(conn, ts); - } - } - - ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - - ++pktns->tx.last_pkt_num; - - return nwrite; -} - -/* - * conn_process_early_rtb makes any pending 0RTT packet Short packet. - */ -static void conn_process_early_rtb(ngtcp2_conn *conn) { - ngtcp2_rtb_entry *ent; - ngtcp2_rtb *rtb = &conn->pktns.rtb; - ngtcp2_ksl_it it; - - for (it = ngtcp2_rtb_head(rtb); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - ent = ngtcp2_ksl_it_get(&it); - - if ((ent->hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) == 0 || - ent->hd.type != NGTCP2_PKT_0RTT) { - continue; - } - - /* 0-RTT packet is retransmitted as a Short packet. */ - ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; - ent->hd.type = NGTCP2_PKT_SHORT; - } -} - -/* - * conn_handshake_remnants_left returns nonzero if there may be - * handshake packets the local endpoint has to send, including new - * packets and lost ones. - */ -static int conn_handshake_remnants_left(ngtcp2_conn *conn) { - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_pktns *hs_pktns = conn->hs_pktns; - - return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || - (in_pktns && (in_pktns->rtb.num_retransmittable || - ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || - (hs_pktns && (hs_pktns->rtb.num_retransmittable || - ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq))); -} - -/* - * conn_retire_dcid_seq retires destination connection ID denoted by - * |seq|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_frame_chain *nfrc; - int rv; - - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; - nfrc->fr.retire_connection_id.seq = seq; - nfrc->next = pktns->tx.frq; - pktns->tx.frq = nfrc; - - ++conn->dcid.num_retire_queued; - - return 0; -} - -/* - * conn_retire_dcid retires |dcid|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, - ngtcp2_tstamp ts) { - ngtcp2_ringbuf *rb = &conn->dcid.retired; - ngtcp2_dcid *dest, *stale_dcid; - int rv; - - if (ngtcp2_ringbuf_full(rb)) { - stale_dcid = ngtcp2_ringbuf_get(rb, 0); - rv = conn_call_deactivate_dcid(conn, stale_dcid); - if (rv != 0) { - return rv; - } - - ngtcp2_ringbuf_pop_front(rb); - } - - dest = ngtcp2_ringbuf_push_back(rb); - ngtcp2_dcid_copy(dest, dcid); - dest->ts_retired = ts; - - return conn_retire_dcid_seq(conn, dcid->seq); -} - -/* - * conn_stop_pv stops the path validation which is currently running. - * This function does nothing if no path validation is currently being - * performed. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - int rv = 0; - ngtcp2_pv *pv = conn->pv; - - if (pv == NULL) { - return 0; - } - - if (pv->dcid.seq != conn->dcid.current.seq) { - rv = conn_retire_dcid(conn, &pv->dcid, ts); - if (rv != 0) { - goto fin; - } - } - - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->fallback_dcid.seq != conn->dcid.current.seq && - pv->fallback_dcid.seq != pv->dcid.seq) { - rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); - if (rv != 0) { - goto fin; - } - } - -fin: - ngtcp2_pv_del(pv); - conn->pv = NULL; - - return rv; -} - -static void conn_reset_congestion_state(ngtcp2_conn *conn); - -/* - * conn_on_path_validation_failed is called when path validation - * fails. This function may delete |pv|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, - ngtcp2_tstamp ts) { - int rv; - - rv = conn_call_path_validation(conn, &pv->dcid.ps.path, - NGTCP2_PATH_VALIDATION_RESULT_FAILURE); - if (rv != 0) { - return rv; - } - - if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { - ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid); - conn_reset_congestion_state(conn); - } - - return conn_stop_pv(conn, ts); -} - -/* - * conn_write_path_challenge writes a packet which includes - * PATH_CHALLENGE frame into |dest| of length |destlen|. - * - * This function returns the number of bytes written to |dest|, or one - * of the following negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, - ngtcp2_path *path, uint8_t *dest, - size_t destlen, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_tstamp expiry; - ngtcp2_pv *pv = conn->pv; - ngtcp2_frame lfr; - - ngtcp2_pv_ensure_start(pv, ts); - - if (ngtcp2_pv_validation_timed_out(pv, ts)) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, - "path validation was timed out"); - return conn_on_path_validation_failed(conn, pv, ts); - } - - ngtcp2_pv_handle_entry_expiry(pv, ts); - - if (ngtcp2_pv_full(pv)) { - return 0; - } - - if (path) { - ngtcp2_path_copy(path, &pv->dcid.ps.path); - } - - assert(conn->callbacks.rand); - rv = conn->callbacks.rand(lfr.path_challenge.data, - sizeof(lfr.path_challenge.data), - NGTCP2_RAND_CTX_PATH_CHALLENGE); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - lfr.type = NGTCP2_FRAME_PATH_CHALLENGE; - - /* TODO reconsider this. This might get larger pretty quickly than - validation timeout which is just around 3*PTO. */ - expiry = ts + 6ULL * conn->cstat.initial_rtt * (1ULL << pv->loss_count); - - ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry); - - return ngtcp2_conn_write_single_frame_pkt( - conn, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr, - NGTCP2_RTB_FLAG_ACK_ELICITING, ts); -} - -ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, - uint8_t *dest, size_t destlen, - ngtcp2_tstamp ts) { - return ngtcp2_conn_writev_stream(conn, path, dest, destlen, - /* pdatalen = */ NULL, - NGTCP2_WRITE_STREAM_FLAG_NONE, - /* stream_id = */ -1, - /* datav = */ NULL, /* datavcnt = */ 0, ts); -} - -/* - * conn_on_version_negotiation is called when Version Negotiation - * packet is received. The function decodes the data in the buffer - * pointed by |payload| whose length is |payloadlen| as Version - * Negotiation packet payload. The packet header is given in |hd|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_INVALID_ARGUMENT - * Packet payload is badly formatted. - */ -static int conn_on_version_negotiation(ngtcp2_conn *conn, - const ngtcp2_pkt_hd *hd, - const uint8_t *payload, - size_t payloadlen) { - uint32_t sv[16]; - uint32_t *p; - int rv = 0; - size_t nsv; - size_t i; - - if (payloadlen % sizeof(uint32_t)) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - if (payloadlen > sizeof(sv)) { - p = ngtcp2_mem_malloc(conn->mem, payloadlen); - if (p == NULL) { - return NGTCP2_ERR_NOMEM; - } - } else { - p = sv; - } - - nsv = ngtcp2_pkt_decode_version_negotiation(p, payload, payloadlen); - - ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); - - for (i = 0; i < nsv; ++i) { - if (p[i] == conn->version) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "ignore Version Negotiation because it contains version " - "selected by client"); - - rv = NGTCP2_ERR_INVALID_ARGUMENT; - goto fin; - } - } - - if (conn->callbacks.recv_version_negotiation) { - rv = conn->callbacks.recv_version_negotiation(conn, hd, p, nsv, - conn->user_data); - if (rv != 0) { - rv = NGTCP2_ERR_CALLBACK_FAILURE; - } - } - - if (rv == 0) { - /* TODO Just move to the terminal state for now in order not to - send CONNECTION_CLOSE frame. */ - conn->state = NGTCP2_CS_DRAINING; - } - -fin: - if (p != sv) { - ngtcp2_mem_free(conn->mem, p); - } - - return rv; -} - -static uint64_t conn_tx_strmq_first_cycle(ngtcp2_conn *conn) { - ngtcp2_strm *strm; - - if (ngtcp2_pq_empty(&conn->tx.strmq)) { - return 0; - } - - strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); - return strm->cycle; -} - -uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn) { - ngtcp2_strm *strm; - - if (ngtcp2_pq_empty(&conn->tx.strmq)) { - return 0; - } - - strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); - return strm->cycle; -} - -int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, - ngtcp2_frame_chain **pfrc) { - ngtcp2_frame_chain **first = pfrc; - ngtcp2_frame_chain *frc; - ngtcp2_stream *sfr; - ngtcp2_strm *strm; - int rv; - - if (*pfrc == NULL) { - return 0; - } - - for (; *pfrc;) { - switch ((*pfrc)->fr.type) { - case NGTCP2_FRAME_STREAM: - frc = *pfrc; - - *pfrc = frc->next; - frc->next = NULL; - sfr = &frc->fr.stream; - - strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); - if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); - break; - } - rv = ngtcp2_strm_streamfrq_push(strm, frc); - if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - if (!ngtcp2_strm_is_tx_queued(strm)) { - strm->cycle = conn_tx_strmq_first_cycle(conn); - rv = ngtcp2_conn_tx_strmq_push(conn, strm); - if (rv != 0) { - return rv; - } - } - break; - case NGTCP2_FRAME_CRYPTO: - frc = *pfrc; - - *pfrc = frc->next; - frc->next = NULL; - - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, - &frc->fr.crypto.offset, frc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - break; - default: - pfrc = &(*pfrc)->next; - } - } - - *pfrc = pktns->tx.frq; - pktns->tx.frq = *first; - - return 0; -} - -/* - * conn_on_retry is called when Retry packet is received. The - * function decodes the data in the buffer pointed by |pkt| whose - * length is |pktlen| as Retry packet. The length of long packet - * header is given in |hdpktlen|. |pkt| includes packet header. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_INVALID_ARGUMENT - * Packet payload is badly formatted. - * NGTCP2_ERR_PROTO - * ODCID does not match; or Token is empty. - */ -static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - size_t hdpktlen, const uint8_t *pkt, size_t pktlen) { - int rv; - ngtcp2_pkt_retry retry; - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_rtb *rtb = &conn->pktns.rtb; - ngtcp2_rtb *in_rtb; - uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1]; - ngtcp2_vec *token; - - if (!in_pktns || conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { - return 0; - } - - in_rtb = &in_pktns->rtb; - - rv = ngtcp2_pkt_decode_retry(&retry, pkt + hdpktlen, pktlen - hdpktlen); - if (rv != 0) { - return rv; - } - - retry.odcid = conn->dcid.current.cid; - - rv = ngtcp2_pkt_verify_retry_tag(&retry, pkt, pktlen, conn->callbacks.encrypt, - &conn->crypto.retry_aead, - &conn->crypto.retry_aead_ctx); - if (rv != 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "unable to verify Retry packet integrity"); - return rv; - } - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s", - (const char *)ngtcp2_encode_hex(cidbuf, retry.odcid.data, - retry.odcid.datalen)); - - if (retry.token.len == 0) { - return NGTCP2_ERR_PROTO; - } - - if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid)) { - return 0; - } - - /* DCID must be updated before invoking callback because client - generates new initial keys there. */ - conn->dcid.current.cid = hd->scid; - conn->retry_scid = hd->scid; - - conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; - - assert(conn->callbacks.recv_retry); - - rv = conn->callbacks.recv_retry(conn, hd, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - conn->state = NGTCP2_CS_CLIENT_INITIAL; - - /* Just freeing memory is dangerous because we might free twice. */ - - rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat); - if (rv != 0) { - return rv; - } - - token = &conn->local.settings.token; - - ngtcp2_mem_free(conn->mem, token->base); - token->base = NULL; - token->len = 0; - - token->base = ngtcp2_mem_malloc(conn->mem, retry.token.len); - if (token->base == NULL) { - return NGTCP2_ERR_NOMEM; - } - token->len = retry.token.len; - - ngtcp2_cpymem(token->base, retry.token.base, retry.token.len); - - reset_conn_stat_recovery(&conn->cstat); - conn_reset_congestion_state(conn); - - return 0; -} - -int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, - ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { - int rv; - ngtcp2_duration pto = conn_compute_pto(conn, pktns); - - rv = ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, pto, ts); - if (rv != 0) { - return rv; - } - - return 0; -} - -/* - * conn_recv_ack processes received ACK frame |fr|. |pkt_ts| is the - * timestamp when packet is received. |ts| should be the current - * time. Usually they are the same, but for buffered packets, - * |pkt_ts| would be earlier than |ts|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_ACK_FRAME - * ACK frame is malformed. - * NGTCP2_ERR_PROTO - * |fr| acknowledges a packet this endpoint has not sent. - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed. - */ -static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, - ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { - int rv; - ngtcp2_frame_chain *frc = NULL; - ngtcp2_ssize num_acked; - ngtcp2_conn_stat *cstat = &conn->cstat; - - if (pktns->tx.last_pkt_num < fr->largest_ack) { - return NGTCP2_ERR_PROTO; - } - - rv = ngtcp2_pkt_validate_ack(fr); - if (rv != 0) { - return rv; - } - - ngtcp2_acktr_recv_ack(&pktns->acktr, fr); - - num_acked = - ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pkt_ts, ts); - if (num_acked < 0) { - /* TODO assert this */ - assert(ngtcp2_err_is_fatal((int)num_acked)); - ngtcp2_frame_chain_list_del(frc, conn->mem); - return (int)num_acked; - } - - if (num_acked == 0) { - return 0; - } - - rv = ngtcp2_conn_detect_lost_pkt(conn, pktns, &conn->cstat, ts); - if (rv != 0) { - return rv; - } - - pktns->rtb.probe_pkt_left = 0; - - if (cstat->pto_count && - (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { - /* Reset PTO count but no less than 2 to avoid frequent probe - packet transmission. */ - cstat->pto_count = ngtcp2_min(cstat->pto_count, 2); - } - - ngtcp2_conn_set_loss_detection_timer(conn, ts); - - return 0; -} - -/* - * conn_assign_recved_ack_delay_unscaled assigns - * fr->ack_delay_unscaled. - */ -static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr, - uint64_t ack_delay_exponent) { - fr->ack_delay_unscaled = - fr->ack_delay * (1UL << ack_delay_exponent) * NGTCP2_MICROSECONDS; -} - -/* - * conn_recv_max_stream_data processes received MAX_STREAM_DATA frame - * |fr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_STREAM_STATE - * Stream ID indicates that it is a local stream, and the local - * endpoint has not initiated it; or stream is peer initiated - * unidirectional stream. - * NGTCP2_ERR_STREAM_LIMIT - * Stream ID exceeds allowed limit. - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_recv_max_stream_data(ngtcp2_conn *conn, - const ngtcp2_max_stream_data *fr) { - ngtcp2_strm *strm; - ngtcp2_idtr *idtr; - int local_stream = conn_local_stream(conn, fr->stream_id); - int bidi = bidi_stream(fr->stream_id); - int rv; - - if (bidi) { - if (local_stream) { - if (conn->local.bidi.next_stream_id <= fr->stream_id) { - return NGTCP2_ERR_STREAM_STATE; - } - } else if (conn->remote.bidi.max_streams < - ngtcp2_ord_stream_id(fr->stream_id)) { - return NGTCP2_ERR_STREAM_LIMIT; - } - - idtr = &conn->remote.bidi.idtr; - } else { - if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) { - return NGTCP2_ERR_STREAM_STATE; - } - - idtr = &conn->remote.uni.idtr; - } - - strm = ngtcp2_conn_find_stream(conn, fr->stream_id); - if (strm == NULL) { - if (local_stream) { - /* Stream has been closed. */ - return 0; - } - - rv = ngtcp2_idtr_open(idtr, fr->stream_id); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - assert(rv == NGTCP2_ERR_STREAM_IN_USE); - /* Stream has been closed. */ - return 0; - } - - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); - if (strm == NULL) { - return NGTCP2_ERR_NOMEM; - } - rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); - if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); - return rv; - } - } - - if (strm->tx.max_offset < fr->max_stream_data) { - strm->tx.max_offset = fr->max_stream_data; - - /* Don't call callback if stream is half-closed local */ - if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { - return 0; - } - - rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id, - fr->max_stream_data); - if (rv != 0) { - return rv; - } - } - - return 0; -} - -/* - * conn_recv_max_data processes received MAX_DATA frame |fr|. - */ -static void conn_recv_max_data(ngtcp2_conn *conn, const ngtcp2_max_data *fr) { - conn->tx.max_offset = ngtcp2_max(conn->tx.max_offset, fr->max_data); -} - -/* - * conn_buffer_pkt buffers |pkt| of length |pktlen|, chaining it from - * |*ppc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_buffer_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, - const ngtcp2_path *path, const uint8_t *pkt, - size_t pktlen, ngtcp2_tstamp ts) { - int rv; - ngtcp2_pkt_chain **ppc = &pktns->rx.buffed_pkts, *pc; - size_t i; - for (i = 0; *ppc && i < NGTCP2_MAX_NUM_BUFFED_RX_PKTS; - ppc = &(*ppc)->next, ++i) - ; - - if (i == NGTCP2_MAX_NUM_BUFFED_RX_PKTS) { - return 0; - } - - rv = ngtcp2_pkt_chain_new(&pc, path, pkt, pktlen, ts, conn->mem); - if (rv != 0) { - return rv; - } - - *ppc = pc; - - return 0; -} - -/* - * conn_ensure_decrypt_buffer ensures that conn->crypto.decrypt_buf - * has at least |n| bytes space. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_ensure_decrypt_buffer(ngtcp2_conn *conn, size_t n) { - uint8_t *nbuf; - size_t len; - ngtcp2_vec *decrypt_buf = &conn->crypto.decrypt_buf; - - if (decrypt_buf->len >= n) { - return 0; - } - - len = decrypt_buf->len == 0 ? 2048 : decrypt_buf->len * 2; - for (; len < n; len *= 2) - ; - nbuf = ngtcp2_mem_realloc(conn->mem, decrypt_buf->base, len); - if (nbuf == NULL) { - return NGTCP2_ERR_NOMEM; - } - decrypt_buf->base = nbuf; - decrypt_buf->len = len; - - return 0; -} - -/* - * decrypt_pkt decrypts the data pointed by |payload| whose length is - * |payloadlen|, and writes plaintext data to the buffer pointed by - * |dest|. The buffer pointed by |ad| is the Additional Data, and its - * length is |adlen|. |pkt_num| is used to create a nonce. |ckm| is - * the cryptographic key, and iv to use. |decrypt| is a callback - * function which actually decrypts a packet. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed. - * NGTCP2_ERR_TLS_DECRYPT - * TLS backend failed to decrypt data. - */ -static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, - const uint8_t *payload, size_t payloadlen, - const uint8_t *ad, size_t adlen, - int64_t pkt_num, ngtcp2_crypto_km *ckm, - ngtcp2_decrypt decrypt, size_t aead_overhead) { - /* TODO nonce is limited to 64 bytes. */ - uint8_t nonce[64]; - int rv; - - assert(sizeof(nonce) >= ckm->iv.len); - - ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num); - - rv = decrypt(dest, aead, &ckm->aead_ctx, payload, payloadlen, nonce, - ckm->iv.len, ad, adlen); - - if (rv != 0) { - if (rv == NGTCP2_ERR_TLS_DECRYPT) { - return rv; - } - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - assert(payloadlen >= aead_overhead); - - return (ngtcp2_ssize)(payloadlen - aead_overhead); -} - -/* - * decrypt_hp decryptes packet header. The packet number starts at - * |pkt| + |pkt_num_offset|. The entire plaintext QUIC packet header - * will be written to the buffer pointed by |dest| whose capacity is - * |destlen|. - * - * This function returns the number of bytes written to |dest|, or one - * of the following negative error codes: - * - * NGTCP2_ERR_PROTO - * Packet is badly formatted - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed; or it does not return - * expected result. - */ -static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, size_t destlen, - const ngtcp2_crypto_cipher *hp, - const uint8_t *pkt, size_t pktlen, - size_t pkt_num_offset, ngtcp2_crypto_km *ckm, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - ngtcp2_hp_mask hp_mask) { - size_t sample_offset; - uint8_t *p = dest; - uint8_t mask[NGTCP2_HP_MASKLEN]; - size_t i; - int rv; - - assert(hp_mask); - assert(ckm); - assert(destlen >= pkt_num_offset + 4); - - if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) { - return NGTCP2_ERR_PROTO; - } - - p = ngtcp2_cpymem(p, pkt, pkt_num_offset); - - sample_offset = pkt_num_offset + 4; - - rv = hp_mask(mask, hp, hp_ctx, pkt + sample_offset); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { - dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0f)); - } else { - dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1f)); - if (dest[0] & NGTCP2_SHORT_KEY_PHASE_BIT) { - hd->flags |= NGTCP2_PKT_FLAG_KEY_PHASE; - } - } - - hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1); - - for (i = 0; i < hd->pkt_numlen; ++i) { - *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1]; - } - - hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen); - - return p - dest; -} - -/* - * conn_emit_pending_crypto_data delivers pending stream data to the - * application due to packet reordering. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed - * NGTCP2_ERR_CRYPTO - * TLS backend reported error - */ -static int conn_emit_pending_crypto_data(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - ngtcp2_strm *strm, - uint64_t rx_offset) { - size_t datalen; - const uint8_t *data; - int rv; - uint64_t offset; - - if (!strm->rx.rob) { - return 0; - } - - for (;;) { - datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset); - if (datalen == 0) { - assert(rx_offset == ngtcp2_strm_rx_offset(strm)); - return 0; - } - - offset = rx_offset; - rx_offset += datalen; - - rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen); - if (rv != 0) { - return rv; - } - - ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen); - } -} - -/* - * conn_recv_connection_close is called when CONNECTION_CLOSE or - * APPLICATION_CLOSE frame is received. - */ -static void conn_recv_connection_close(ngtcp2_conn *conn, - ngtcp2_connection_close *fr) { - conn->state = NGTCP2_CS_DRAINING; - if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { - conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; - } else { - conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; - } - conn->rx.ccec.error_code = fr->error_code; -} - -static void conn_recv_path_challenge(ngtcp2_conn *conn, - ngtcp2_path_challenge *fr) { - ngtcp2_path_challenge_entry *ent; - - ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge); - ngtcp2_path_challenge_entry_init(ent, fr->data); -} - -/* - * conn_reset_congestion_state resets congestion state. - */ -static void conn_reset_congestion_state(ngtcp2_conn *conn) { - conn_reset_conn_stat_cc(conn, &conn->cstat); - - conn_reset_rx_rate(conn); - - conn->cc.reset(&conn->cc); - - if (conn->hs_pktns) { - ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb, - conn->hs_pktns->tx.last_pkt_num + 1); - } - ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1); - ngtcp2_rst_init(&conn->rst); -} - -static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_pv *pv = conn->pv; - - if (!pv) { - return 0; - } - - rv = ngtcp2_pv_validate(pv, fr->data); - if (rv != 0) { - if (rv == NGTCP2_ERR_PATH_VALIDATION_FAILED) { - return conn_on_path_validation_failed(conn, pv, ts); - } - return 0; - } - - if (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { - rv = conn_retire_dcid(conn, &conn->dcid.current, ts); - if (rv != 0) { - goto fail; - } - ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); - conn_reset_congestion_state(conn); - } - - rv = conn_call_path_validation(conn, &pv->dcid.ps.path, - NGTCP2_PATH_VALIDATION_RESULT_SUCCESS); - if (rv != 0) { - goto fail; - } - -fail: - return conn_stop_pv(conn, ts); -} - -/* - * pkt_num_bits returns the number of bits available when packet - * number is encoded in |pkt_numlen| bytes. - */ -static size_t pkt_num_bits(size_t pkt_numlen) { - switch (pkt_numlen) { - case 1: - return 8; - case 2: - return 16; - case 3: - return 24; - case 4: - return 32; - default: - assert(0); - } -} - -/* - * pktns_pkt_num_is_duplicate returns nonzero if |pkt_num| is - * duplicated packet number. - */ -static int pktns_pkt_num_is_duplicate(ngtcp2_pktns *pktns, int64_t pkt_num) { - return ngtcp2_gaptr_is_pushed(&pktns->rx.pngap, (uint64_t)pkt_num, 1); -} - -/* - * pktns_commit_recv_pkt_num marks packet number |pkt_num| as - * received. - */ -static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num, - ngtcp2_tstamp ts) { - int rv; - - if (pktns->rx.max_pkt_num + 1 != pkt_num) { - ngtcp2_acktr_immediate_ack(&pktns->acktr); - } - if (pktns->rx.max_pkt_num < pkt_num) { - pktns->rx.max_pkt_num = pkt_num; - pktns->rx.max_pkt_ts = ts; - } - - rv = ngtcp2_gaptr_push(&pktns->rx.pngap, (uint64_t)pkt_num, 1); - if (rv != 0) { - return rv; - } - - if (ngtcp2_ksl_len(&pktns->rx.pngap.gap) > 256) { - ngtcp2_gaptr_drop_first_gap(&pktns->rx.pngap); - } - - return 0; -} - -/* - * verify_token verifies |hd| contains |token| in its token field. It - * returns 0 if it succeeds, or NGTCP2_ERR_PROTO. - */ -static int verify_token(const ngtcp2_vec *token, const ngtcp2_pkt_hd *hd) { - if (token->len == hd->token.len && - ngtcp2_cmemeq(token->base, hd->token.base, token->len)) { - return 0; - } - return NGTCP2_ERR_PROTO; -} - -static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, - ngtcp2_strm *strm, const ngtcp2_crypto *fr); - -static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts); - -static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, - ngtcp2_pktns *pktns, - ngtcp2_tstamp ts); - -/* - * conn_recv_handshake_pkt processes received packet |pkt| whose - * length is |pktlen| during handshake period. The buffer pointed by - * |pkt| might contain multiple packets. This function only processes - * one packet. |pkt_ts| is the timestamp when packet is received. - * |ts| should be the current time. Usually they are the same, but - * for buffered packets, |pkt_ts| would be earlier than |ts|. - * - * This function returns the number of bytes it reads if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_RECV_VERSION_NEGOTIATION - * Version Negotiation packet is received. - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_DISCARD_PKT - * Packet was discarded because plain text header was malformed; - * or its payload could not be decrypted. - * NGTCP2_ERR_FRAME_FORMAT - * Frame is badly formatted - * NGTCP2_ERR_ACK_FRAME - * ACK frame is malformed. - * NGTCP2_ERR_CRYPTO - * TLS stack reported error. - * NGTCP2_ERR_PROTO - * Generic QUIC protocol error. - * - * In addition to the above error codes, error codes returned from - * conn_recv_pkt are also returned. - */ -static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, - const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp pkt_ts, - ngtcp2_tstamp ts) { - ngtcp2_ssize nread; - ngtcp2_pkt_hd hd; - ngtcp2_max_frame mfr; - ngtcp2_frame *fr = &mfr.fr; - int rv; - int require_ack = 0; - size_t hdpktlen; - const uint8_t *payload; - size_t payloadlen; - ngtcp2_ssize nwrite; - uint8_t plain_hdpkt[1500]; - ngtcp2_crypto_aead *aead; - ngtcp2_crypto_cipher *hp; - ngtcp2_crypto_km *ckm; - ngtcp2_crypto_cipher_ctx *hp_ctx; - ngtcp2_hp_mask hp_mask; - ngtcp2_decrypt decrypt; - size_t aead_overhead; - ngtcp2_pktns *pktns; - ngtcp2_strm *crypto; - ngtcp2_crypto_level crypto_level; - int invalid_reserved_bits = 0; - - if (pktlen == 0) { - return 0; - } - - if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { - if (conn->state == NGTCP2_CS_SERVER_INITIAL) { - /* Ignore Short packet unless server's first Handshake packet - has been transmitted. */ - return (ngtcp2_ssize)pktlen; - } - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "buffering Short packet len=%zu", pktlen); - - rv = conn_buffer_pkt(conn, &conn->pktns, path, pkt, pktlen, ts); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - return (ngtcp2_ssize)pktlen; - } - - nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); - if (nread < 0) { - return NGTCP2_ERR_DISCARD_PKT; - } - - switch (hd.type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: - hdpktlen = (size_t)nread; - - ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - - if (conn->server) { - return NGTCP2_ERR_DISCARD_PKT; - } - - /* Receiving Version Negotiation packet after getting Handshake - packet from server is invalid. */ - if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { - return NGTCP2_ERR_DISCARD_PKT; - } - - if (!ngtcp2_cid_eq(&conn->oscid, &hd.dcid)) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched DCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - - if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { - /* Just discard invalid Version Negotiation packet */ - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched SCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - rv = conn_on_version_negotiation(conn, &hd, pkt + hdpktlen, - pktlen - hdpktlen); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - return NGTCP2_ERR_DISCARD_PKT; - } - return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; - case NGTCP2_PKT_RETRY: - hdpktlen = (size_t)nread; - - ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - - if (conn->server) { - return NGTCP2_ERR_DISCARD_PKT; - } - - /* Receiving Retry packet after getting Initial packet from server - is invalid. */ - if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { - return NGTCP2_ERR_DISCARD_PKT; - } - - rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - return NGTCP2_ERR_DISCARD_PKT; - } - return (ngtcp2_ssize)pktlen; - } - - if (pktlen < (size_t)nread + hd.len) { - return NGTCP2_ERR_DISCARD_PKT; - } - - pktlen = (size_t)nread + hd.len; - - if (conn->version != hd.version) { - return NGTCP2_ERR_DISCARD_PKT; - } - - /* Quoted from spec: if subsequent packets of those types include a - different Source Connection ID, they MUST be discarded. */ - if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) && - !ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { - ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched SCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - - switch (hd.type) { - case NGTCP2_PKT_0RTT: - if (!conn->server) { - return NGTCP2_ERR_DISCARD_PKT; - } - if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { - if (conn->early.ckm) { - ngtcp2_ssize nread2; - /* TODO Avoid to parse header twice. */ - nread2 = conn_recv_pkt(conn, &conn->dcid.current.ps.path, pkt, pktlen, - pkt_ts, ts); - if (nread2 < 0) { - return nread2; - } - } - - /* Discard 0-RTT packet if we don't have a key to decrypt it. */ - return (ngtcp2_ssize)pktlen; - } - - /* Buffer re-ordered 0-RTT packet. */ - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "buffering 0-RTT packet len=%zu", pktlen); - - rv = conn_buffer_pkt(conn, conn->in_pktns, path, pkt, pktlen, ts); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - return (ngtcp2_ssize)pktlen; - case NGTCP2_PKT_INITIAL: - if (!conn->in_pktns) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_PKT, - "Initial packet is discarded because keys have been discarded"); - return (ngtcp2_ssize)pktlen; - } - - assert(conn->in_pktns); - - if (conn->server) { - if (conn->local.settings.token.len) { - rv = verify_token(&conn->local.settings.token, &hd); - if (rv != 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because token is invalid"); - return NGTCP2_ERR_DISCARD_PKT; - } - } - if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) == 0) { - /* Set rcid here so that it is available to callback. If this - packet is discarded later in this function and no packet is - processed in this connection attempt so far, connection - will be dropped. */ - conn->rcid = hd.dcid; - - rv = conn_call_recv_client_initial(conn, &hd.dcid); - if (rv != 0) { - return rv; - } - } - } else if (hd.token.len != 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because token is not empty"); - return NGTCP2_ERR_DISCARD_PKT; - } - - pktns = conn->in_pktns; - aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; - crypto = &pktns->crypto.strm; - crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; - - break; - case NGTCP2_PKT_HANDSHAKE: - if (!conn->hs_pktns->crypto.rx.ckm) { - if (conn->server) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_PKT, - "Handshake packet at this point is unexpected and discarded"); - return (ngtcp2_ssize)pktlen; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "buffering Handshake packet len=%zu", pktlen); - - rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pkt, pktlen, ts); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - return (ngtcp2_ssize)pktlen; - } - - pktns = conn->hs_pktns; - aead_overhead = conn->crypto.aead_overhead; - crypto = &pktns->crypto.strm; - crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; - - break; - default: - /* unknown packet type */ - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of unknown packet type"); - return (ngtcp2_ssize)pktlen; - } - - hp_mask = conn->callbacks.hp_mask; - decrypt = conn->callbacks.decrypt; - aead = &pktns->crypto.ctx.aead; - hp = &pktns->crypto.ctx.hp; - ckm = pktns->crypto.rx.ckm; - hp_ctx = &pktns->crypto.rx.hp_ctx; - - assert(ckm); - assert(hp_mask); - assert(decrypt); - - nwrite = decrypt_hp(&hd, plain_hdpkt, sizeof(plain_hdpkt), hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); - if (nwrite < 0) { - if (ngtcp2_err_is_fatal((int)nwrite)) { - return nwrite; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "could not decrypt packet number"); - return NGTCP2_ERR_DISCARD_PKT; - } - - hdpktlen = (size_t)nwrite; - payload = pkt + hdpktlen; - payloadlen = hd.len - hd.pkt_numlen; - - hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, - pkt_num_bits(hd.pkt_numlen)); - if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num); - return NGTCP2_ERR_DISCARD_PKT; - } - - ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - - rv = ngtcp2_pkt_verify_reserved_bits(plain_hdpkt[0]); - if (rv != 0) { - invalid_reserved_bits = 1; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet has incorrect reserved bits"); - - /* Will return error after decrypting payload */ - } - - if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was discarded because of duplicated packet number"); - return NGTCP2_ERR_DISCARD_PKT; - } - - rv = conn_ensure_decrypt_buffer(conn, payloadlen); - if (rv != 0) { - return rv; - } - - nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen, - plain_hdpkt, hdpktlen, hd.pkt_num, ckm, decrypt, - aead_overhead); - if (nwrite < 0) { - if (ngtcp2_err_is_fatal((int)nwrite)) { - return nwrite; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "could not decrypt packet payload"); - return NGTCP2_ERR_DISCARD_PKT; - } - - if (invalid_reserved_bits) { - return NGTCP2_ERR_PROTO; - } - - payload = conn->crypto.decrypt_buf.base; - payloadlen = (size_t)nwrite; - - switch (hd.type) { - case NGTCP2_PKT_INITIAL: - if (!conn->server || ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) && - !ngtcp2_cid_eq(&conn->rcid, &hd.dcid))) { - rv = conn_verify_dcid(conn, NULL, &hd); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched DCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - } - break; - case NGTCP2_PKT_HANDSHAKE: - rv = conn_verify_dcid(conn, NULL, &hd); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched DCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - break; - default: - assert(0); - } - - if (payloadlen == 0) { - /* QUIC packet must contain at least one frame */ - return NGTCP2_ERR_DISCARD_PKT; - } - - if (hd.type == NGTCP2_PKT_INITIAL && - !(conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED)) { - conn->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED; - if (!conn->server) { - conn->dcid.current.cid = hd.scid; - } - } - - ngtcp2_qlog_pkt_received_start(&conn->qlog, &hd); - - for (; payloadlen;) { - nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); - if (nread < 0) { - return nread; - } - - payload += nread; - payloadlen -= (size_t)nread; - - if (fr->type == NGTCP2_FRAME_ACK) { - fr->ack.ack_delay = 0; - fr->ack.ack_delay_unscaled = 0; - } - - ngtcp2_log_rx_fr(&conn->log, &hd, fr); - - switch (fr->type) { - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { - conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; - } - rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); - if (rv != 0) { - return rv; - } - break; - case NGTCP2_FRAME_PADDING: - break; - case NGTCP2_FRAME_CRYPTO: - rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto); - if (rv != 0) { - return rv; - } - require_ack = 1; - break; - case NGTCP2_FRAME_CONNECTION_CLOSE: - conn_recv_connection_close(conn, &fr->connection_close); - break; - case NGTCP2_FRAME_PING: - require_ack = 1; - break; - default: - return NGTCP2_ERR_PROTO; - } - - ngtcp2_qlog_write_frame(&conn->qlog, fr); - } - - if (conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { - /* Successful processing of Handshake packet from client verifies - source address. */ - conn->flags |= NGTCP2_CONN_FLAG_SADDR_VERIFIED; - } - - ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); - - rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, pkt_ts); - if (rv != 0) { - return rv; - } - - if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) { - ngtcp2_acktr_immediate_ack(&pktns->acktr); - } - - rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, - pkt_ts); - if (rv != 0) { - return rv; - } - - conn_restart_timer_on_read(conn, ts); - - ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - - return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING - : (ngtcp2_ssize)pktlen; -} - -/* - * conn_recv_handshake_cpkt processes compound packet during - * handshake. The buffer pointed by |pkt| might contain multiple - * packets. The Short packet must be the last one because it does not - * have payload length field. - * - * This function returns the same error code returned by - * conn_recv_handshake_pkt. - */ -static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts) { - ngtcp2_ssize nread; - - conn->cstat.bytes_recv += pktlen; - - while (pktlen) { - nread = conn_recv_handshake_pkt(conn, path, pkt, pktlen, ts, ts); - if (nread < 0) { - if (ngtcp2_err_is_fatal((int)nread)) { - return (int)nread; - } - - if (nread == NGTCP2_ERR_DRAINING) { - return NGTCP2_ERR_DRAINING; - } - - if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && - /* Not a Version Negotiation packet */ - pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 && - ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) { - if (conn->server) { - /* If server discards first Initial, then drop connection - state. This is because SCID in packet might be corrupted - and the current connection state might wrongly discard - valid packet and prevent the handshake from - completing. */ - if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { - /* If this is crypto related error, then return normally - in order to send CONNECTION_CLOSE with TLS alert (e.g., - no_application_protocol). */ - if (nread == NGTCP2_ERR_CRYPTO) { - return (int)nread; - } - return NGTCP2_ERR_DROP_CONN; - } - } else if (nread == NGTCP2_ERR_CRYPTO) { - /* If client gets crypto error from TLS stack, it is - unrecoverable, therefore drop connection. */ - return (int)nread; - } - return 0; - } - - if (nread == NGTCP2_ERR_DISCARD_PKT) { - return 0; - } - - return (int)nread; - } - - assert(pktlen >= (size_t)nread); - pkt += nread; - pktlen -= (size_t)nread; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "read packet %td left %zu", nread, pktlen); - } - - return 0; -} - -int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - int64_t stream_id, void *stream_user_data) { - int rv; - uint64_t max_rx_offset; - uint64_t max_tx_offset; - int local_stream = conn_local_stream(conn, stream_id); - - if (bidi_stream(stream_id)) { - if (local_stream) { - max_rx_offset = conn->local.settings.transport_params - .initial_max_stream_data_bidi_local; - max_tx_offset = - conn->remote.transport_params.initial_max_stream_data_bidi_remote; - } else { - max_rx_offset = conn->local.settings.transport_params - .initial_max_stream_data_bidi_remote; - max_tx_offset = - conn->remote.transport_params.initial_max_stream_data_bidi_local; - } - } else if (local_stream) { - max_rx_offset = 0; - max_tx_offset = conn->remote.transport_params.initial_max_stream_data_uni; - } else { - max_rx_offset = - conn->local.settings.transport_params.initial_max_stream_data_uni; - max_tx_offset = 0; - } - - rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, - max_tx_offset, stream_user_data, conn->mem); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_map_insert(&conn->strms, &strm->me); - if (rv != 0) { - assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); - goto fail; - } - - if (!conn_local_stream(conn, stream_id)) { - rv = conn_call_stream_open(conn, strm); - if (rv != 0) { - goto fail; - } - } - - return 0; - -fail: - ngtcp2_strm_free(strm); - return rv; -} - -/* - * conn_emit_pending_stream_data passes buffered ordered stream data - * to the application. |rx_offset| is the first offset to deliver to - * the application. This function assumes that the data up to - * |rx_offset| has been delivered already. This function only passes - * the ordered data without any gap. If there is a gap, it stops - * providing the data to the application, and returns. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed. - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t rx_offset) { - size_t datalen; - const uint8_t *data; - int rv; - uint64_t offset; - uint32_t sdflags; - int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; - - if (!strm->rx.rob) { - return 0; - } - - for (;;) { - /* Stop calling callback if application has called - ngtcp2_conn_shutdown_stream_read() inside the callback. - Because it doubly counts connection window. */ - if (strm->flags & (NGTCP2_STRM_FLAG_STOP_SENDING)) { - return 0; - } - - datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset); - if (datalen == 0) { - assert(rx_offset == ngtcp2_strm_rx_offset(strm)); - return 0; - } - - offset = rx_offset; - rx_offset += datalen; - - sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && - rx_offset == strm->rx.last_offset) { - sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; - } - if (!handshake_completed) { - sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; - } - - rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, datalen); - if (rv != 0) { - return rv; - } - - ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen); - } -} - -/* - * conn_recv_crypto is called when CRYPTO frame |fr| is received. - * |rx_offset_base| is the offset in the entire TLS handshake stream. - * fr->offset specifies the offset in each encryption level. - * |max_rx_offset| is, if it is nonzero, the maximum offset in the - * entire TLS handshake stream that |fr| can carry. |crypto_level| is - * the encryption level where this data is received. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_PROTO - * CRYPTO frame has invalid offset. - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CRYPTO - * TLS stack reported error. - * NGTCP2_ERR_FRAME_ENCODING - * The end offset exceeds the maximum value. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, - ngtcp2_strm *crypto, const ngtcp2_crypto *fr) { - uint64_t fr_end_offset; - uint64_t rx_offset; - int rv; - - if (fr->datacnt == 0) { - return 0; - } - - fr_end_offset = fr->offset + fr->data[0].len; - - if (NGTCP2_MAX_VARINT < fr_end_offset) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - rx_offset = ngtcp2_strm_rx_offset(crypto); - - if (fr_end_offset <= rx_offset) { - if (conn->server && crypto_level == NGTCP2_CRYPTO_LEVEL_INITIAL) { - /* recovery draft: Speeding Up Handshake Completion - - When a server receives an Initial packet containing duplicate - CRYPTO data, it can assume the client did not receive all of - the server's CRYPTO data sent in Initial packets, or the - client's estimated RTT is too small. ... To speed up - handshake completion under these conditions, an endpoint MAY - send a packet containing unacknowledged CRYPTO data earlier - than the PTO expiry, subject to address validation limits; - ... */ - conn->in_pktns->rtb.probe_pkt_left = 1; - conn->hs_pktns->rtb.probe_pkt_left = 1; - } - return 0; - } - - crypto->rx.last_offset = ngtcp2_max(crypto->rx.last_offset, fr_end_offset); - - /* TODO Before dispatching incoming data to TLS stack, make sure - that previous data in previous encryption level has been - completely sent to TLS stack. Usually, if data is left, it is an - error because key is generated after consuming all data in the - previous encryption level. */ - if (fr->offset <= rx_offset) { - size_t ncut = (size_t)(rx_offset - fr->offset); - const uint8_t *data = fr->data[0].base + ncut; - size_t datalen = fr->data[0].len - ncut; - uint64_t offset = rx_offset; - - rx_offset += datalen; - rv = ngtcp2_strm_update_rx_offset(crypto, rx_offset); - if (rv != 0) { - return rv; - } - - rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen); - if (rv != 0) { - return rv; - } - - rv = conn_emit_pending_crypto_data(conn, crypto_level, crypto, rx_offset); - if (rv != 0) { - return rv; - } - } else if (fr_end_offset - rx_offset > NGTCP2_MAX_REORDERED_CRYPTO_DATA) { - return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED; - } else { - rv = ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len, - fr->offset); - if (rv != 0) { - return rv; - } - } - - return 0; -} - -/* - * conn_max_data_violated returns nonzero if receiving |datalen| - * violates connection flow control on local endpoint. - */ -static int conn_max_data_violated(ngtcp2_conn *conn, uint64_t datalen) { - return conn->rx.max_offset - conn->rx.offset < datalen; -} - -/* - * conn_recv_stream is called when STREAM frame |fr| is received. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_STREAM_STATE - * STREAM frame is received for a local stream which is not - * initiated; or STREAM frame is received for a local - * unidirectional stream - * NGTCP2_ERR_STREAM_LIMIT - * STREAM frame has remote stream ID which is strictly greater - * than the allowed limit. - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_FLOW_CONTROL - * Flow control limit is violated; or the end offset of stream - * data is beyond the NGTCP2_MAX_VARINT. - * NGTCP2_ERR_FINAL_SIZE - * STREAM frame has strictly larger end offset than it is - * permitted. - */ -static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_strm *strm; - ngtcp2_idtr *idtr; - uint64_t rx_offset, fr_end_offset; - int local_stream; - int bidi; - size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); - uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; - - local_stream = conn_local_stream(conn, fr->stream_id); - bidi = bidi_stream(fr->stream_id); - - if (bidi) { - if (local_stream) { - if (conn->local.bidi.next_stream_id <= fr->stream_id) { - return NGTCP2_ERR_STREAM_STATE; - } - } else if (conn->remote.bidi.max_streams < - ngtcp2_ord_stream_id(fr->stream_id)) { - return NGTCP2_ERR_STREAM_LIMIT; - } - - idtr = &conn->remote.bidi.idtr; - } else { - if (local_stream) { - return NGTCP2_ERR_STREAM_STATE; - } - if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { - return NGTCP2_ERR_STREAM_LIMIT; - } - - idtr = &conn->remote.uni.idtr; - } - - if (NGTCP2_MAX_VARINT - datalen < fr->offset) { - return NGTCP2_ERR_FLOW_CONTROL; - } - - strm = ngtcp2_conn_find_stream(conn, fr->stream_id); - if (strm == NULL) { - if (local_stream) { - /* TODO The stream has been closed. This should be responded - with RESET_STREAM, or simply ignored. */ - return 0; - } - - rv = ngtcp2_idtr_open(idtr, fr->stream_id); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - assert(rv == NGTCP2_ERR_STREAM_IN_USE); - /* TODO The stream has been closed. This should be responded - with RESET_STREAM, or simply ignored. */ - return 0; - } - - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); - if (strm == NULL) { - return NGTCP2_ERR_NOMEM; - } - /* TODO Perhaps, call new_stream callback? */ - rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); - if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); - return rv; - } - if (!bidi) { - ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); - } - } - - fr_end_offset = fr->offset + datalen; - - if (strm->rx.max_offset < fr_end_offset) { - return NGTCP2_ERR_FLOW_CONTROL; - } - - if (strm->rx.last_offset < fr_end_offset) { - uint64_t len = fr_end_offset - strm->rx.last_offset; - - if (conn_max_data_violated(conn, len)) { - return NGTCP2_ERR_FLOW_CONTROL; - } - - conn->rx.offset += len; - - if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { - ngtcp2_conn_extend_max_offset(conn, len); - } - } - - rx_offset = ngtcp2_strm_rx_offset(strm); - - if (fr->fin) { - if (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) { - if (strm->rx.last_offset != fr_end_offset) { - return NGTCP2_ERR_FINAL_SIZE; - } - - if (strm->flags & - (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { - return 0; - } - - if (rx_offset == fr_end_offset) { - return 0; - } - } else if (strm->rx.last_offset > fr_end_offset) { - return NGTCP2_ERR_FINAL_SIZE; - } else { - strm->rx.last_offset = fr_end_offset; - - ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); - - if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, - strm->app_error_code); - } - } - } else { - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && - strm->rx.last_offset < fr_end_offset) { - return NGTCP2_ERR_FINAL_SIZE; - } - - strm->rx.last_offset = ngtcp2_max(strm->rx.last_offset, fr_end_offset); - - if (fr_end_offset <= rx_offset) { - return 0; - } - - if (strm->flags & - (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { - return 0; - } - } - - conn_update_recv_rate(conn, fr_end_offset - fr->offset, ts); - - if (fr->offset <= rx_offset) { - size_t ncut = (size_t)(rx_offset - fr->offset); - uint64_t offset = rx_offset; - const uint8_t *data; - int fin; - - if (fr->datacnt) { - data = fr->data[0].base + ncut; - datalen -= ncut; - - rx_offset += datalen; - rv = ngtcp2_strm_update_rx_offset(strm, rx_offset); - if (rv != 0) { - return rv; - } - } else { - data = NULL; - datalen = 0; - } - - 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->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; - } - rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, - 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) { - rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len, - fr->offset); - if (rv != 0) { - return rv; - } - } - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); -} - -/* - * conn_reset_stream adds RESET_STREAM frame to the transmission - * queue. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { - int rv; - ngtcp2_frame_chain *frc; - ngtcp2_pktns *pktns = &conn->pktns; - - rv = ngtcp2_frame_chain_new(&frc, conn->mem); - if (rv != 0) { - return rv; - } - - frc->fr.type = NGTCP2_FRAME_RESET_STREAM; - frc->fr.reset_stream.stream_id = strm->stream_id; - frc->fr.reset_stream.app_error_code = app_error_code; - frc->fr.reset_stream.final_size = strm->tx.offset; - - /* TODO This prepends RESET_STREAM to pktns->tx.frq. */ - frc->next = pktns->tx.frq; - pktns->tx.frq = frc; - - return 0; -} - -/* - * conn_stop_sending adds STOP_SENDING frame to the transmission - * queue. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { - int rv; - ngtcp2_frame_chain *frc; - ngtcp2_pktns *pktns = &conn->pktns; - - rv = ngtcp2_frame_chain_new(&frc, conn->mem); - if (rv != 0) { - return rv; - } - - frc->fr.type = NGTCP2_FRAME_STOP_SENDING; - frc->fr.stop_sending.stream_id = strm->stream_id; - frc->fr.stop_sending.app_error_code = app_error_code; - - /* TODO This prepends STOP_SENDING to pktns->tx.frq. */ - frc->next = pktns->tx.frq; - pktns->tx.frq = frc; - - return 0; -} - -/* - * handle_max_remote_streams_extension extends - * |*punsent_max_remote_streams| by |n| if a condition allows it. - */ -static void -handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams, - size_t n) { - if ( -#if SIZE_MAX > UINT32_MAX - NGTCP2_MAX_STREAMS < n || -#endif /* SIZE_MAX > UINT32_MAX */ - *punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) { - *punsent_max_remote_streams = NGTCP2_MAX_STREAMS; - } else { - *punsent_max_remote_streams += n; - } -} - -/* - * conn_recv_reset_stream is called when RESET_STREAM |fr| is - * received. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_STREAM_STATE - * RESET_STREAM frame is received to the local stream which is not - * initiated. - * NGTCP2_ERR_STREAM_LIMIT - * RESET_STREAM frame has remote stream ID which is strictly - * greater than the allowed limit. - * NGTCP2_ERR_PROTO - * RESET_STREAM frame is received to the local unidirectional - * stream - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_FLOW_CONTROL - * Flow control limit is violated; or the final size is beyond the - * NGTCP2_MAX_VARINT. - * NGTCP2_ERR_FINAL_SIZE - * The final offset is strictly larger than it is permitted. - */ -static int conn_recv_reset_stream(ngtcp2_conn *conn, - const ngtcp2_reset_stream *fr) { - ngtcp2_strm *strm; - int local_stream = conn_local_stream(conn, fr->stream_id); - int bidi = bidi_stream(fr->stream_id); - uint64_t datalen; - ngtcp2_idtr *idtr; - int rv; - - /* TODO share this piece of code */ - if (bidi) { - if (local_stream) { - if (conn->local.bidi.next_stream_id <= fr->stream_id) { - return NGTCP2_ERR_STREAM_STATE; - } - } else if (conn->remote.bidi.max_streams < - ngtcp2_ord_stream_id(fr->stream_id)) { - return NGTCP2_ERR_STREAM_LIMIT; - } - - idtr = &conn->remote.bidi.idtr; - } else { - if (local_stream) { - return NGTCP2_ERR_PROTO; - } - if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { - return NGTCP2_ERR_STREAM_LIMIT; - } - - idtr = &conn->remote.uni.idtr; - } - - if (NGTCP2_MAX_VARINT < fr->final_size) { - return NGTCP2_ERR_FLOW_CONTROL; - } - - strm = ngtcp2_conn_find_stream(conn, fr->stream_id); - if (strm == NULL) { - if (local_stream) { - return 0; - } - - if (conn_initial_stream_rx_offset(conn, fr->stream_id) < fr->final_size || - conn_max_data_violated(conn, fr->final_size)) { - return NGTCP2_ERR_FLOW_CONTROL; - } - rv = ngtcp2_idtr_open(idtr, fr->stream_id); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - assert(rv == NGTCP2_ERR_STREAM_IN_USE); - return 0; - } - - /* Stream is reset before we create ngtcp2_strm object. */ - conn->rx.offset += fr->final_size; - ngtcp2_conn_extend_max_offset(conn, fr->final_size); - - rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, - fr->app_error_code, NULL); - if (rv != 0) { - return rv; - } - - /* There will be no activity in this stream because we got - RESET_STREAM and don't write stream data any further. This - effectively allows another new stream for peer. */ - if (bidi) { - handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, - 1); - } else { - handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, - 1); - } - - return 0; - } - - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { - if (strm->rx.last_offset != fr->final_size) { - return NGTCP2_ERR_FINAL_SIZE; - } - } else if (strm->rx.last_offset > fr->final_size) { - return NGTCP2_ERR_FINAL_SIZE; - } - - datalen = fr->final_size - strm->rx.last_offset; - - if (strm->rx.max_offset < fr->final_size || - conn_max_data_violated(conn, datalen)) { - return NGTCP2_ERR_FLOW_CONTROL; - } - - if (!(strm->flags & NGTCP2_STRM_FLAG_RECV_RST)) { - rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, - fr->app_error_code, strm->stream_user_data); - if (rv != 0) { - return rv; - } - - /* Extend connection flow control window for the amount of data - which are not passed to application. */ - if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { - ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - - ngtcp2_strm_rx_offset(strm)); - } - } - - conn->rx.offset += datalen; - ngtcp2_conn_extend_max_offset(conn, datalen); - - strm->rx.last_offset = fr->final_size; - strm->flags |= NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_RECV_RST; - - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); -} - -/* - * conn_recv_stop_sending is called when STOP_SENDING |fr| is received. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_STREAM_STATE - * STOP_SENDING frame is received for a local stream which is not - * initiated; or STOP_SENDING frame is received for a local - * unidirectional stream. - * NGTCP2_ERR_STREAM_LIMIT - * STOP_SENDING frame has remote stream ID which is strictly - * greater than the allowed limit. - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_recv_stop_sending(ngtcp2_conn *conn, - const ngtcp2_stop_sending *fr) { - int rv; - ngtcp2_strm *strm; - ngtcp2_idtr *idtr; - int local_stream = conn_local_stream(conn, fr->stream_id); - int bidi = bidi_stream(fr->stream_id); - - if (bidi) { - if (local_stream) { - if (conn->local.bidi.next_stream_id <= fr->stream_id) { - return NGTCP2_ERR_STREAM_STATE; - } - } else if (conn->remote.bidi.max_streams < - ngtcp2_ord_stream_id(fr->stream_id)) { - return NGTCP2_ERR_STREAM_LIMIT; - } - - idtr = &conn->remote.bidi.idtr; - } else { - if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) { - return NGTCP2_ERR_STREAM_STATE; - } - - idtr = &conn->remote.uni.idtr; - } - - strm = ngtcp2_conn_find_stream(conn, fr->stream_id); - if (strm == NULL) { - if (local_stream) { - return 0; - } - rv = ngtcp2_idtr_open(idtr, fr->stream_id); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - assert(rv == NGTCP2_ERR_STREAM_IN_USE); - return 0; - } - - /* Frame is received reset before we create ngtcp2_strm - object. */ - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); - if (strm == NULL) { - return NGTCP2_ERR_NOMEM; - } - rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); - if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); - return rv; - } - } - - /* No RESET_STREAM is required if we have sent FIN and all data have - been acknowledged. */ - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) && - ngtcp2_strm_is_all_tx_data_acked(strm)) { - return 0; - } - - rv = conn_reset_stream(conn, strm, fr->app_error_code); - if (rv != 0) { - return rv; - } - - strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; - - ngtcp2_strm_streamfrq_clear(strm); - - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); -} - -/* - * check_stateless_reset returns nonzero if Stateless Reset |sr| - * coming via |path| is valid against |dcid|. - */ -static int check_stateless_reset(const ngtcp2_dcid *dcid, - const ngtcp2_path *path, - const ngtcp2_pkt_stateless_reset *sr) { - return ngtcp2_path_eq(&dcid->ps.path, path) && - ngtcp2_verify_stateless_reset_token(dcid->token, - sr->stateless_reset_token) == 0; -} - -/* - * conn_on_stateless_reset decodes Stateless Reset from the buffer - * pointed by |payload| whose length is |payloadlen|. |payload| - * should start after first byte of packet. - * - * If Stateless Reset is decoded, and the Stateless Reset Token is - * validated, the connection is closed. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Could not decode Stateless Reset; or Stateless Reset Token does - * not match; or No stateless reset token is available. - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed. - */ -static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, - const uint8_t *payload, size_t payloadlen) { - int rv = 1; - ngtcp2_pv *pv = conn->pv; - ngtcp2_dcid *dcid; - ngtcp2_pkt_stateless_reset sr; - size_t len, i; - - rv = ngtcp2_pkt_decode_stateless_reset(&sr, payload, payloadlen); - if (rv != 0) { - return rv; - } - - if (!check_stateless_reset(&conn->dcid.current, path, &sr) && - (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) && - (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || - !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) { - len = ngtcp2_ringbuf_len(&conn->dcid.retired); - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); - if (check_stateless_reset(dcid, path, &sr)) { - break; - } - } - - if (i == len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - } - - conn->state = NGTCP2_CS_DRAINING; - - ngtcp2_log_rx_sr(&conn->log, &sr); - - if (!conn->callbacks.recv_stateless_reset) { - return 0; - } - - rv = conn->callbacks.recv_stateless_reset(conn, &sr, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -/* - * conn_recv_max_streams processes the incoming MAX_STREAMS frame - * |fr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed. - * NGTCP2_ERR_FRAME_ENCODING - * The maximum streams field exceeds the maximum value. - */ -static int conn_recv_max_streams(ngtcp2_conn *conn, - const ngtcp2_max_streams *fr) { - uint64_t n; - - if (fr->max_streams > NGTCP2_MAX_STREAMS) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - n = ngtcp2_min(fr->max_streams, NGTCP2_MAX_STREAMS); - - if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { - if (conn->local.bidi.max_streams < n) { - conn->local.bidi.max_streams = n; - return conn_call_extend_max_local_streams_bidi(conn, n); - } - return 0; - } - - if (conn->local.uni.max_streams < n) { - conn->local.uni.max_streams = n; - return conn_call_extend_max_local_streams_uni(conn, n); - } - return 0; -} - -static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, - uint64_t retire_prior_to) { - size_t i; - ngtcp2_dcid *dcid, *last; - int rv; - - for (i = 0; i < ngtcp2_ringbuf_len(rb);) { - dcid = ngtcp2_ringbuf_get(rb, i); - if (dcid->seq >= retire_prior_to) { - ++i; - continue; - } - - rv = conn_retire_dcid_seq(conn, dcid->seq); - if (rv != 0) { - return rv; - } - if (i == 0) { - ngtcp2_ringbuf_pop_front(rb); - } else if (i == ngtcp2_ringbuf_len(rb) - 1) { - ngtcp2_ringbuf_pop_back(rb); - break; - } else { - last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); - ngtcp2_dcid_copy(dcid, last); - ngtcp2_ringbuf_pop_back(rb); - } - } - - return 0; -} - -/* - * conn_recv_new_connection_id processes the incoming - * NEW_CONNECTION_ID frame |fr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_PROTO - * |fr| has the duplicated sequence number with different CID or - * token; or DCID is zero-length. - */ -static int conn_recv_new_connection_id(ngtcp2_conn *conn, - const ngtcp2_new_connection_id *fr) { - size_t i, len; - ngtcp2_dcid *dcid; - ngtcp2_pv *pv = conn->pv; - int rv; - int found = 0; - size_t extra_dcid = 0; - - if (conn->dcid.current.cid.datalen == 0) { - return NGTCP2_ERR_PROTO; - } - - if (fr->retire_prior_to > fr->seq) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - rv = ngtcp2_dcid_verify_uniqueness(&conn->dcid.current, fr->seq, &fr->cid, - fr->stateless_reset_token); - if (rv != 0) { - return rv; - } - if (ngtcp2_cid_eq(&conn->dcid.current.cid, &fr->cid)) { - found = 1; - } - - if (pv) { - rv = ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid, - fr->stateless_reset_token); - if (rv != 0) { - return rv; - } - if (ngtcp2_cid_eq(&pv->dcid.cid, &fr->cid)) { - found = 1; - } - } - - len = ngtcp2_ringbuf_len(&conn->dcid.unused); - - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i); - rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, - fr->stateless_reset_token); - if (rv != 0) { - return NGTCP2_ERR_PROTO; - } - if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) { - found = 1; - } - } - - if (conn->dcid.retire_prior_to < fr->retire_prior_to) { - conn->dcid.retire_prior_to = fr->retire_prior_to; - - rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, - conn->dcid.retire_prior_to); - if (rv != 0) { - return rv; - } - } else if (fr->seq < conn->dcid.retire_prior_to) { - /* If packets are reordered, we might have retire_prior_to which - is larger than fr->seq. - - A malicious peer might send crafted NEW_CONNECTION_ID to force - local endpoint to create lots of RETIRE_CONNECTION_ID frames. - For example, a peer might send seq = 50000 and retire_prior_to - = 50000. Then send NEW_CONNECTION_ID frames with seq < - 50000. */ - if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) { - return conn_retire_dcid_seq(conn, fr->seq); - } - return 0; - } - - if (found) { - return 0; - } - - if (ngtcp2_gaptr_is_pushed(&conn->dcid.seqgap, fr->seq, 1)) { - return 0; - } - - rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, fr->seq, 1); - if (rv != 0) { - return rv; - } - - if (ngtcp2_ksl_len(&conn->dcid.seqgap.gap) > 32) { - ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap); - } - - len = ngtcp2_ringbuf_len(&conn->dcid.unused); - - if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { - ++extra_dcid; - } - if (pv) { - if (pv->dcid.seq != conn->dcid.current.seq && - pv->dcid.seq >= conn->dcid.retire_prior_to) { - ++extra_dcid; - } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->fallback_dcid.seq != conn->dcid.current.seq && - pv->fallback_dcid.seq >= conn->dcid.retire_prior_to) { - ++extra_dcid; - } - } - - if (conn->local.settings.transport_params.active_connection_id_limit <= - len + extra_dcid) { - return NGTCP2_ERR_CONNECTION_ID_LIMIT; - } - - if (len >= NGTCP2_MAX_DCID_POOL_SIZE) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "too many connection ID"); - return 0; - } - - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); - ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); - - return 0; -} - -/* - * conn_post_process_recv_new_connection_id handles retirement request - * of active DCIDs. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, - ngtcp2_tstamp ts) { - ngtcp2_pv *pv = conn->pv; - ngtcp2_dcid *dcid; - int rv; - - if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { - return 0; - } - - rv = conn_retire_dcid(conn, &conn->dcid.current, ts); - if (rv != 0) { - return rv; - } - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - if (pv) { - if (conn->dcid.current.seq == pv->dcid.seq) { - ngtcp2_dcid_copy_no_path(&pv->dcid, dcid); - } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - conn->dcid.current.seq == pv->fallback_dcid.seq) { - ngtcp2_dcid_copy_no_path(&pv->fallback_dcid, dcid); - } - } - - ngtcp2_dcid_copy_no_path(&conn->dcid.current, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - - rv = conn_call_activate_dcid(conn, &conn->dcid.current); - if (rv != 0) { - return rv; - } - } - - if (pv) { - if (pv->dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { - rv = conn_retire_dcid(conn, &pv->dcid, ts); - if (rv != 0) { - return rv; - } - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->dcid.seq == pv->fallback_dcid.seq) { - ngtcp2_dcid_copy_no_path(&pv->fallback_dcid, dcid); - } - - ngtcp2_dcid_copy_no_path(&pv->dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - - rv = conn_call_activate_dcid(conn, &pv->dcid); - if (rv != 0) { - return rv; - } - } else { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, - "path migration is aborted because connection ID is" - "retired and no unused connection ID is available"); - - return conn_stop_pv(conn, ts); - } - } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { - rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); - if (rv != 0) { - return rv; - } - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - ngtcp2_dcid_copy_no_path(&pv->fallback_dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - - rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); - if (rv != 0) { - return rv; - } - } else { - /* Now we have no fallback dcid. */ - return conn_stop_pv(conn, ts); - } - } - } - - return 0; -} - -/* - * conn_recv_retire_connection_id processes the incoming - * RETIRE_CONNECTION_ID frame |fr|. |hd| is a packet header which - * |fr| is included. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_PROTO - * SCID is zero-length. - * NGTCP2_ERR_FRAME_ENCODING - * Attempt to retire CID which is used as DCID to send this frame. - */ -static int conn_recv_retire_connection_id(ngtcp2_conn *conn, - const ngtcp2_pkt_hd *hd, - const ngtcp2_retire_connection_id *fr, - ngtcp2_tstamp ts) { - ngtcp2_ksl_it it; - ngtcp2_scid *scid; - - if (conn->oscid.datalen == 0 || conn->scid.last_seq < fr->seq) { - return NGTCP2_ERR_PROTO; - } - - for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - scid = ngtcp2_ksl_it_get(&it); - if (scid->seq == fr->seq) { - if (ngtcp2_cid_eq(&scid->cid, &hd->dcid)) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - if (!(scid->flags & NGTCP2_SCID_FLAG_RETIRED)) { - scid->flags |= NGTCP2_SCID_FLAG_RETIRED; - ++conn->scid.num_retired; - } - - if (scid->pe.index != NGTCP2_PQ_BAD_INDEX) { - ngtcp2_pq_remove(&conn->scid.used, &scid->pe); - scid->pe.index = NGTCP2_PQ_BAD_INDEX; - } - - scid->ts_retired = ts; - - return ngtcp2_pq_push(&conn->scid.used, &scid->pe); - } - } - - return 0; -} - -/* - * conn_recv_new_token processes the incoming NEW_TOKEN frame |fr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Token is empty - * NGTCP2_ERR_PROTO: - * Server received NEW_TOKEN. - */ -static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { - int rv; - - if (conn->server) { - return NGTCP2_ERR_PROTO; - } - - if (fr->token.len == 0) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - if (conn->callbacks.recv_new_token) { - rv = conn->callbacks.recv_new_token(conn, &fr->token, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - - return 0; -} - -/* - * conn_select_preferred_addr asks a client application to select a - * server address from preferred addresses received from server. If a - * client chooses the address, path validation will start. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_select_preferred_addr(ngtcp2_conn *conn) { - struct sockaddr_storage buf; - ngtcp2_addr addr; - int rv; - ngtcp2_duration timeout; - ngtcp2_pv *pv; - ngtcp2_dcid *dcid; - - ngtcp2_addr_init(&addr, (struct sockaddr *)&buf, 0, NULL); - - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { - return 0; - } - - rv = conn_call_select_preferred_addr(conn, &addr); - if (rv != 0) { - return rv; - } - - if (addr.addrlen == 0 || - ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) { - return 0; - } - - assert(conn->pv == NULL); - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - timeout = 3 * conn_compute_pto(conn, &conn->pktns); - timeout = ngtcp2_max(timeout, 6 * conn->cstat.initial_rtt); - - rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log, - conn->mem); - if (rv != 0) { - /* TODO Call ngtcp2_dcid_free here if it is introduced */ - return rv; - } - - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - conn->pv = pv; - - ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local); - ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr); - - return conn_call_activate_dcid(conn, &pv->dcid); -} - -/* - * conn_recv_handshake_done processes the incoming HANDSHAKE_DONE - * frame |fr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_PROTO - * Server received HANDSHAKE_DONE frame. - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - int rv; - - if (conn->server) { - return NGTCP2_ERR_PROTO; - } - - if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { - return 0; - } - - conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED | - NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; - - conn->pktns.rtb.persistent_congestion_start_ts = ts; - - conn_discard_handshake_state(conn, ts); - - if (conn->remote.transport_params.preferred_address_present) { - rv = conn_select_preferred_addr(conn); - if (rv != 0) { - return rv; - } - } - - if (conn->callbacks.handshake_confirmed) { - rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - - /* Re-arm loss detection timer after handshake has been - confirmed. */ - ngtcp2_conn_set_loss_detection_timer(conn, ts); - - return 0; -} - -/* - * conn_key_phase_changed returns nonzero if |hd| indicates that the - * key phase has unexpected value. - */ -static int conn_key_phase_changed(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { - ngtcp2_pktns *pktns = &conn->pktns; - - return !(pktns->crypto.rx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) ^ - !(hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE); -} - -/* - * conn_prepare_key_update installs new updated keys. - */ -static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - int rv; - ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; - ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_crypto_km *rx_ckm = pktns->crypto.rx.ckm; - ngtcp2_crypto_km *tx_ckm = pktns->crypto.tx.ckm; - ngtcp2_crypto_km *new_rx_ckm, *new_tx_ckm; - ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}, tx_aead_ctx = {0}; - size_t secretlen, ivlen; - - if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && - (tx_ckm->use_count >= pktns->crypto.ctx.max_encryption || - rx_ckm->use_count >= pktns->crypto.ctx.max_decryption_failure)) { - ngtcp2_conn_initiate_key_update(conn, ts); - } - - if ((conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || - (confirmed_ts != UINT64_MAX && confirmed_ts + pto > ts)) { - return 0; - } - - if (conn->crypto.key_update.new_rx_ckm || - conn->crypto.key_update.new_tx_ckm) { - assert(conn->crypto.key_update.new_rx_ckm); - assert(conn->crypto.key_update.new_tx_ckm); - return 0; - } - - secretlen = rx_ckm->secret.len; - ivlen = rx_ckm->iv.len; - - rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_rx_ckm, - secretlen, ivlen, conn->mem); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_tx_ckm, - secretlen, ivlen, conn->mem); - if (rv != 0) { - return rv; - } - - new_rx_ckm = conn->crypto.key_update.new_rx_ckm; - new_tx_ckm = conn->crypto.key_update.new_tx_ckm; - - assert(conn->callbacks.update_key); - - rv = conn->callbacks.update_key( - conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, &rx_aead_ctx, - new_rx_ckm->iv.base, &tx_aead_ctx, new_tx_ckm->iv.base, - rx_ckm->secret.base, tx_ckm->secret.base, secretlen, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - new_rx_ckm->aead_ctx = rx_aead_ctx; - new_tx_ckm->aead_ctx = tx_aead_ctx; - - if (!(rx_ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)) { - new_rx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; - new_tx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; - } - - if (conn->crypto.key_update.old_rx_ckm) { - conn_call_delete_crypto_aead_ctx( - conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx); - ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); - conn->crypto.key_update.old_rx_ckm = NULL; - } - - return 0; -} - -/* - * conn_rotate_keys rotates keys. The current key moves to old key, - * and new key moves to the current key. - */ -static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { - ngtcp2_pktns *pktns = &conn->pktns; - - assert(conn->crypto.key_update.new_rx_ckm); - assert(conn->crypto.key_update.new_tx_ckm); - assert(!conn->crypto.key_update.old_rx_ckm); - assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); - - conn->crypto.key_update.old_rx_ckm = pktns->crypto.rx.ckm; - - pktns->crypto.rx.ckm = conn->crypto.key_update.new_rx_ckm; - conn->crypto.key_update.new_rx_ckm = NULL; - pktns->crypto.rx.ckm->pkt_num = pkt_num; - - assert(pktns->crypto.tx.ckm); - - conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); - ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); - - pktns->crypto.tx.ckm = conn->crypto.key_update.new_tx_ckm; - conn->crypto.key_update.new_tx_ckm = NULL; - pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1; - - conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; -} - -/* - * conn_path_validation_in_progress returns nonzero if path validation - * against |path| is underway. - */ -static int conn_path_validation_in_progress(ngtcp2_conn *conn, - const ngtcp2_path *path) { - ngtcp2_pv *pv = conn->pv; - - return pv && ngtcp2_path_eq(&pv->dcid.ps.path, path); -} - -/* - * conn_recv_non_probing_pkt_on_new_path is called when non-probing - * packet is received via new path. It starts path validation against - * the new path. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_CONN_ID_BLOCKED - * No DCID is available - * NGTCP2_ERR_NOMEM - * Out of memory - */ -static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, - const ngtcp2_path *path, - int new_cid_used, - ngtcp2_tstamp ts) { - - ngtcp2_dcid dcid; - ngtcp2_pv *pv; - int rv; - ngtcp2_duration timeout; - int require_new_cid; - - assert(conn->server); - - if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)) { - /* If new path equals fallback path, that means connection - migrated back to the original path. Fallback path is - considered to be validated. */ - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, - "path is migrated back to the original path"); - ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid); - conn_reset_congestion_state(conn); - rv = conn_stop_pv(conn, ts); - if (rv != 0) { - return rv; - } - return 0; - } - - /* The transport specification draft-27 says: - * - * An endpoint MUST use a new connection ID if it initiates - * connection migration as described in Section 9.2 or probes a new - * network path as described in Section 9.1. An endpoint MUST use a - * new connection ID in response to a change in the address of a - * peer if the packet with the new peer address uses an active - * connection ID that has not been previously used by the peer. - */ - require_new_cid = - (new_cid_used && - !ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &path->remote)) || - !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local); - - /* If the remote endpoint uses new DCID, server has to change its - DCID as well. */ - if (require_new_cid && ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { - return NGTCP2_ERR_CONN_ID_BLOCKED; - } - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "non-probing packet was received from new remote address"); - - timeout = 3 * conn_compute_pto(conn, &conn->pktns); - timeout = ngtcp2_max(timeout, 6 * conn->cstat.initial_rtt); - - if (require_new_cid) { - dcid = *(ngtcp2_dcid *)ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - - rv = conn_call_activate_dcid(conn, &dcid); - if (rv != 0) { - return rv; - } - } else { - /* Use the current DCID if a remote endpoint does not change - DCID. */ - dcid = conn->dcid.current; - } - - ngtcp2_path_copy(&dcid.ps.path, path); - - rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, - &conn->log, conn->mem); - if (rv != 0) { - return rv; - } - - if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { - ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid); - } else { - ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); - } - - ngtcp2_dcid_copy(&conn->dcid.current, &dcid); - - conn_reset_congestion_state(conn); - - if (conn->pv) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_PTV, - "path migration is aborted because new migration has started"); - rv = conn_stop_pv(conn, ts); - if (rv != 0) { - return rv; - } - } - - conn->pv = pv; - - return 0; -} - -/* - * conn_recv_delayed_handshake_pkt processes the received Handshake - * packet which is received after handshake completed. This function - * does the minimal job, and its purpose is send acknowledgement of - * this packet to the peer. We assume that hd->type == - * NGTCP2_PKT_HANDSHAKE. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Frame is badly formatted; or frame type is unknown. - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_DISCARD_PKT - * Packet was discarded. - * NGTCP2_ERR_ACK_FRAME - * ACK frame is malformed. - * NGTCP2_ERR_PROTO - * Frame that is not allowed in Handshake packet is received. - */ -static int -conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - size_t pktlen, const uint8_t *payload, - size_t payloadlen, ngtcp2_tstamp pkt_ts, - ngtcp2_tstamp ts) { - ngtcp2_ssize nread; - ngtcp2_max_frame mfr; - ngtcp2_frame *fr = &mfr.fr; - int rv; - int require_ack = 0; - ngtcp2_pktns *pktns; - - assert(hd->type == NGTCP2_PKT_HANDSHAKE); - - pktns = conn->hs_pktns; - - if (payloadlen == 0) { - /* QUIC packet must contain at least one frame */ - return NGTCP2_ERR_DISCARD_PKT; - } - - ngtcp2_qlog_pkt_received_start(&conn->qlog, hd); - - for (; payloadlen;) { - nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); - if (nread < 0) { - return (int)nread; - } - - payload += nread; - payloadlen -= (size_t)nread; - - if (fr->type == NGTCP2_FRAME_ACK) { - fr->ack.ack_delay = 0; - fr->ack.ack_delay_unscaled = 0; - } - - ngtcp2_log_rx_fr(&conn->log, hd, fr); - - switch (fr->type) { - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - if (!conn->server) { - conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; - } - rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); - if (rv != 0) { - return rv; - } - break; - case NGTCP2_FRAME_PADDING: - break; - case NGTCP2_FRAME_CONNECTION_CLOSE: - conn_recv_connection_close(conn, &fr->connection_close); - break; - case NGTCP2_FRAME_CRYPTO: - case NGTCP2_FRAME_PING: - require_ack = 1; - break; - default: - return NGTCP2_ERR_PROTO; - } - - ngtcp2_qlog_write_frame(&conn->qlog, fr); - } - - ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen); - - rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num, pkt_ts); - if (rv != 0) { - return rv; - } - - if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) { - ngtcp2_acktr_immediate_ack(&pktns->acktr); - } - - rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd->pkt_num, require_ack, - pkt_ts); - if (rv != 0) { - return rv; - } - - conn_restart_timer_on_read(conn, ts); - - ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - - return 0; -} - -/* - * conn_recv_pkt processes a packet contained in the buffer pointed by - * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets. - * This function only processes the first packet. |pkt_ts| is the - * timestamp when packet is received. |ts| should be the current - * time. Usually they are the same, but for buffered packets, - * |pkt_ts| would be earlier than |ts|. - * - * This function returns the number of bytes processed if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_DISCARD_PKT - * Packet was discarded because plain text header was malformed; - * or its payload could not be decrypted. - * NGTCP2_ERR_PROTO - * Packet is badly formatted; or 0RTT packet contains other than - * PADDING or STREAM frames; or other QUIC protocol violation is - * found. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_FRAME_ENCODING - * Frame is badly formatted; or frame type is unknown. - * NGTCP2_ERR_ACK_FRAME - * ACK frame is malformed. - * NGTCP2_ERR_STREAM_STATE - * Frame is received to the local stream which is not initiated. - * NGTCP2_ERR_STREAM_LIMIT - * Frame has remote stream ID which is strictly greater than the - * allowed limit. - * NGTCP2_ERR_FLOW_CONTROL - * Flow control limit is violated. - * NGTCP2_ERR_FINAL_SIZE - * Frame has strictly larger end offset than it is permitted. - */ -static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { - ngtcp2_pkt_hd hd; - int rv = 0; - size_t hdpktlen; - const uint8_t *payload; - size_t payloadlen; - ngtcp2_ssize nread, nwrite; - ngtcp2_max_frame mfr; - ngtcp2_frame *fr = &mfr.fr; - int require_ack = 0; - ngtcp2_crypto_aead *aead; - ngtcp2_crypto_cipher *hp; - ngtcp2_crypto_km *ckm; - ngtcp2_crypto_cipher_ctx *hp_ctx; - uint8_t plain_hdpkt[1500]; - ngtcp2_hp_mask hp_mask; - ngtcp2_decrypt decrypt; - size_t aead_overhead; - ngtcp2_pktns *pktns; - int non_probing_pkt = 0; - int key_phase_bit_changed = 0; - int force_decrypt_failure = 0; - int recv_ncid = 0; - int new_cid_used = 0; - - if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { - nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); - if (nread < 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "could not decode long header"); - return NGTCP2_ERR_DISCARD_PKT; - } - - if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) { - return NGTCP2_ERR_DISCARD_PKT; - } - - pktlen = (size_t)nread + hd.len; - - /* Quoted from spec: if subsequent packets of those types include - a different Source Connection ID, they MUST be discarded. */ - if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { - ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched SCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - - switch (hd.type) { - case NGTCP2_PKT_INITIAL: - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "delayed Initial packet was discarded"); - return (ngtcp2_ssize)pktlen; - case NGTCP2_PKT_HANDSHAKE: - if (!conn->hs_pktns) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "delayed Handshake packet was discarded"); - return (ngtcp2_ssize)pktlen; - } - - pktns = conn->hs_pktns; - ckm = pktns->crypto.rx.ckm; - hp_ctx = &pktns->crypto.rx.hp_ctx; - hp_mask = conn->callbacks.hp_mask; - decrypt = conn->callbacks.decrypt; - aead_overhead = conn->crypto.aead_overhead; - break; - case NGTCP2_PKT_0RTT: - if (!conn->server || !conn->early.ckm) { - return NGTCP2_ERR_DISCARD_PKT; - } - - pktns = &conn->pktns; - ckm = conn->early.ckm; - hp_ctx = &conn->early.hp_ctx; - hp_mask = conn->callbacks.hp_mask; - decrypt = conn->callbacks.decrypt; - aead_overhead = conn->crypto.aead_overhead; - break; - default: - ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet type 0x%02x was ignored", hd.type); - return (ngtcp2_ssize)pktlen; - } - } else { - nread = ngtcp2_pkt_decode_hd_short(&hd, pkt, pktlen, conn->oscid.datalen); - if (nread < 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "could not decode short header"); - return NGTCP2_ERR_DISCARD_PKT; - } - - pktns = &conn->pktns; - ckm = pktns->crypto.rx.ckm; - hp_ctx = &pktns->crypto.rx.hp_ctx; - hp_mask = conn->callbacks.hp_mask; - decrypt = conn->callbacks.decrypt; - aead_overhead = conn->crypto.aead_overhead; - } - - aead = &pktns->crypto.ctx.aead; - hp = &pktns->crypto.ctx.hp; - - nwrite = decrypt_hp(&hd, plain_hdpkt, sizeof(plain_hdpkt), hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); - if (nwrite < 0) { - if (ngtcp2_err_is_fatal((int)nwrite)) { - return nwrite; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "could not decrypt packet number"); - return NGTCP2_ERR_DISCARD_PKT; - } - - hdpktlen = (size_t)nwrite; - payload = pkt + hdpktlen; - payloadlen = pktlen - hdpktlen; - - hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, - pkt_num_bits(hd.pkt_numlen)); - if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num); - return NGTCP2_ERR_DISCARD_PKT; - } - - ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - - if (hd.type == NGTCP2_PKT_SHORT) { - key_phase_bit_changed = conn_key_phase_changed(conn, &hd); - } - - rv = conn_ensure_decrypt_buffer(conn, payloadlen); - if (rv != 0) { - return rv; - } - - if (key_phase_bit_changed) { - assert(hd.type == NGTCP2_PKT_SHORT); - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE"); - - if (ckm->pkt_num > hd.pkt_num) { - if (conn->crypto.key_update.old_rx_ckm) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "decrypting with old key"); - ckm = conn->crypto.key_update.old_rx_ckm; - } else { - force_decrypt_failure = 1; - } - } else if (pktns->rx.max_pkt_num < hd.pkt_num) { - assert(ckm->pkt_num < hd.pkt_num); - if (!conn->crypto.key_update.new_rx_ckm) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "new key is not available"); - force_decrypt_failure = 1; - } else { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "decrypting with new key"); - ckm = conn->crypto.key_update.new_rx_ckm; - } - } else { - force_decrypt_failure = 1; - } - } - - nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen, - plain_hdpkt, hdpktlen, hd.pkt_num, ckm, decrypt, - aead_overhead); - - if (force_decrypt_failure) { - nwrite = NGTCP2_ERR_TLS_DECRYPT; - } - - if (nwrite < 0) { - if (ngtcp2_err_is_fatal((int)nwrite)) { - return nwrite; - } - - assert(NGTCP2_ERR_TLS_DECRYPT == nwrite); - - ++ckm->use_count; - - if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "could not decrypt packet payload"); - return NGTCP2_ERR_DISCARD_PKT; - } - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "could not decrypt packet payload"); - return NGTCP2_ERR_DISCARD_PKT; - } - - rv = ngtcp2_pkt_verify_reserved_bits(plain_hdpkt[0]); - if (rv != 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet has incorrect reserved bits"); - - return NGTCP2_ERR_PROTO; - } - - if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was discarded because of duplicated packet number"); - return NGTCP2_ERR_DISCARD_PKT; - } - - payload = conn->crypto.decrypt_buf.base; - payloadlen = (size_t)nwrite; - - if (payloadlen == 0) { - /* QUIC packet must contain at least one frame */ - return NGTCP2_ERR_DISCARD_PKT; - } - - if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { - switch (hd.type) { - case NGTCP2_PKT_HANDSHAKE: - rv = conn_verify_dcid(conn, NULL, &hd); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched DCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - - rv = conn_recv_delayed_handshake_pkt(conn, &hd, pktlen, payload, - payloadlen, pkt_ts, ts); - if (rv < 0) { - return (ngtcp2_ssize)rv; - } - - return (ngtcp2_ssize)pktlen; - case NGTCP2_PKT_0RTT: - if (!ngtcp2_cid_eq(&conn->rcid, &hd.dcid)) { - rv = conn_verify_dcid(conn, NULL, &hd); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched DCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - } - break; - default: - /* Unreachable */ - assert(0); - } - } else { - rv = conn_verify_dcid(conn, &new_cid_used, &hd); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because of mismatched DCID"); - return NGTCP2_ERR_DISCARD_PKT; - } - - conn->flags |= NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT; - } - - ngtcp2_qlog_pkt_received_start(&conn->qlog, &hd); - - for (; payloadlen;) { - nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); - if (nread < 0) { - return nread; - } - - payload += nread; - payloadlen -= (size_t)nread; - - if (fr->type == NGTCP2_FRAME_ACK) { - if ((hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) && - hd.type == NGTCP2_PKT_0RTT) { - return NGTCP2_ERR_PROTO; - } - assign_recved_ack_delay_unscaled( - &fr->ack, conn->remote.transport_params.ack_delay_exponent); - } - - ngtcp2_log_rx_fr(&conn->log, &hd, fr); - - if (hd.type == NGTCP2_PKT_0RTT) { - switch (fr->type) { - case NGTCP2_FRAME_PADDING: - case NGTCP2_FRAME_PING: - case NGTCP2_FRAME_RESET_STREAM: - case NGTCP2_FRAME_STOP_SENDING: - case NGTCP2_FRAME_STREAM: - case NGTCP2_FRAME_MAX_DATA: - case NGTCP2_FRAME_MAX_STREAM_DATA: - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - case NGTCP2_FRAME_MAX_STREAMS_UNI: - case NGTCP2_FRAME_DATA_BLOCKED: - case NGTCP2_FRAME_STREAM_DATA_BLOCKED: - case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: - case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: - case NGTCP2_FRAME_NEW_CONNECTION_ID: - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - case NGTCP2_FRAME_PATH_CHALLENGE: - case NGTCP2_FRAME_PATH_RESPONSE: - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - break; - default: - return NGTCP2_ERR_PROTO; - } - } - - switch (fr->type) { - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - case NGTCP2_FRAME_PADDING: - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - break; - default: - require_ack = 1; - } - - switch (fr->type) { - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - if (!conn->server) { - conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; - } - rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_STREAM: - rv = conn_recv_stream(conn, &fr->stream, ts); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_CRYPTO: - rv = conn_recv_crypto(conn, NGTCP2_CRYPTO_LEVEL_APP, &pktns->crypto.strm, - &fr->crypto); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_RESET_STREAM: - rv = conn_recv_reset_stream(conn, &fr->reset_stream); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_STOP_SENDING: - rv = conn_recv_stop_sending(conn, &fr->stop_sending); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_MAX_STREAM_DATA: - rv = conn_recv_max_stream_data(conn, &fr->max_stream_data); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_MAX_DATA: - conn_recv_max_data(conn, &fr->max_data); - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - case NGTCP2_FRAME_MAX_STREAMS_UNI: - rv = conn_recv_max_streams(conn, &fr->max_streams); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - conn_recv_connection_close(conn, &fr->connection_close); - break; - case NGTCP2_FRAME_PING: - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_PATH_CHALLENGE: - conn_recv_path_challenge(conn, &fr->path_challenge); - break; - case NGTCP2_FRAME_PATH_RESPONSE: - rv = conn_recv_path_response(conn, &fr->path_response, ts); - if (rv != 0) { - return rv; - } - break; - case NGTCP2_FRAME_NEW_CONNECTION_ID: - rv = conn_recv_new_connection_id(conn, &fr->new_connection_id); - if (rv != 0) { - return rv; - } - recv_ncid = 1; - break; - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - rv = conn_recv_retire_connection_id(conn, &hd, &fr->retire_connection_id, - ts); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_NEW_TOKEN: - rv = conn_recv_new_token(conn, &fr->new_token); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_HANDSHAKE_DONE: - rv = conn_recv_handshake_done(conn, ts); - if (rv != 0) { - return rv; - } - non_probing_pkt = 1; - break; - case NGTCP2_FRAME_DATA_BLOCKED: - case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: - case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: - /* TODO Not implemented yet */ - non_probing_pkt = 1; - break; - } - - ngtcp2_qlog_write_frame(&conn->qlog, fr); - } - - ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); - - if (recv_ncid) { - rv = conn_post_process_recv_new_connection_id(conn, ts); - if (rv != 0) { - return rv; - } - } - - if (conn->server && hd.type == NGTCP2_PKT_SHORT && non_probing_pkt && - pktns->rx.max_pkt_num < hd.pkt_num && - !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && - !conn_path_validation_in_progress(conn, path)) { - rv = conn_recv_non_probing_pkt_on_new_path(conn, path, new_cid_used, ts); - if (rv != 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - - /* DCID is not available. Just continue. */ - assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv); - } - } - - if (hd.type == NGTCP2_PKT_SHORT) { - if (ckm == conn->crypto.key_update.new_rx_ckm) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys"); - conn_rotate_keys(conn, hd.pkt_num); - } else if (ckm->pkt_num > hd.pkt_num) { - ckm->pkt_num = hd.pkt_num; - } - } - - rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, pkt_ts); - if (rv != 0) { - return rv; - } - - if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) { - ngtcp2_acktr_immediate_ack(&pktns->acktr); - } - - rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, - pkt_ts); - if (rv != 0) { - return rv; - } - - conn_restart_timer_on_read(conn, ts); - - ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); - - return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING - : (ngtcp2_ssize)pktlen; -} - -/* - * conn_process_buffered_protected_pkt processes buffered 0RTT or - * Short packets. - * - * This function returns 0 if it succeeds, or the same negative error - * codes from conn_recv_pkt. - */ -static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, - ngtcp2_pktns *pktns, - ngtcp2_tstamp ts) { - ngtcp2_ssize nread; - ngtcp2_pkt_chain **ppc, *next; - int rv; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "processing buffered protected packet"); - - for (ppc = &pktns->rx.buffed_pkts; *ppc;) { - next = (*ppc)->next; - nread = conn_recv_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt, (*ppc)->pktlen, - (*ppc)->ts, ts); - if (nread < 0 && !ngtcp2_err_is_fatal((int)nread) && - nread != NGTCP2_ERR_DRAINING) { - /* TODO We don't know this is the first QUIC packet in a - datagram. */ - rv = conn_on_stateless_reset(conn, &(*ppc)->path.path, (*ppc)->pkt, - (*ppc)->pktlen); - if (rv == 0) { - ngtcp2_pkt_chain_del(*ppc, conn->mem); - *ppc = next; - return NGTCP2_ERR_DRAINING; - } - } - - ngtcp2_pkt_chain_del(*ppc, conn->mem); - *ppc = next; - if (nread < 0) { - if (nread == NGTCP2_ERR_DISCARD_PKT) { - continue; - } - return (int)nread; - } - } - - return 0; -} - -/* - * conn_process_buffered_handshake_pkt processes buffered Handshake - * packets. - * - * This function returns 0 if it succeeds, or the same negative error - * codes from conn_recv_handshake_pkt. - */ -static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn, - ngtcp2_tstamp ts) { - ngtcp2_pktns *pktns = conn->hs_pktns; - ngtcp2_ssize nread; - ngtcp2_pkt_chain **ppc, *next; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "processing buffered handshake packet"); - - for (ppc = &pktns->rx.buffed_pkts; *ppc;) { - next = (*ppc)->next; - nread = conn_recv_handshake_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt, - (*ppc)->pktlen, (*ppc)->ts, ts); - ngtcp2_pkt_chain_del(*ppc, conn->mem); - *ppc = next; - if (nread < 0) { - if (nread == NGTCP2_ERR_DISCARD_PKT) { - continue; - } - return (int)nread; - } - } - - return 0; -} - -static void conn_sync_stream_id_limit(ngtcp2_conn *conn) { - ngtcp2_transport_params *params = &conn->remote.transport_params; - - conn->local.bidi.max_streams = params->initial_max_streams_bidi; - conn->local.uni.max_streams = params->initial_max_streams_uni; -} - -/* - * conn_handshake_completed is called once cryptographic handshake has - * completed. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed. - */ -static int conn_handshake_completed(ngtcp2_conn *conn) { - int rv; - - conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED; - - rv = conn_call_handshake_completed(conn); - if (rv != 0) { - return rv; - } - - if (conn->local.bidi.max_streams > 0) { - rv = conn_call_extend_max_local_streams_bidi(conn, - conn->local.bidi.max_streams); - if (rv != 0) { - return rv; - } - } - if (conn->local.uni.max_streams > 0) { - rv = conn_call_extend_max_local_streams_uni(conn, - conn->local.uni.max_streams); - if (rv != 0) { - return rv; - } - } - - return 0; -} - -/* - * conn_recv_cpkt processes compound packet after handshake. The - * buffer pointed by |pkt| might contain multiple packets. The Short - * packet must be the last one because it does not have payload length - * field. - * - * This function returns 0 if it succeeds, or the same negative error - * codes from conn_recv_pkt except for NGTCP2_ERR_DISCARD_PKT. - */ -static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) { - ngtcp2_ssize nread; - int rv; - const uint8_t *origpkt = pkt; - size_t origpktlen = pktlen; - - conn->cstat.bytes_recv += pktlen; - - while (pktlen) { - nread = conn_recv_pkt(conn, path, pkt, pktlen, ts, ts); - if (nread < 0) { - if (ngtcp2_err_is_fatal((int)nread)) { - return (int)nread; - } - - if (nread == NGTCP2_ERR_DRAINING) { - return NGTCP2_ERR_DRAINING; - } - - if (origpkt == pkt) { - rv = conn_on_stateless_reset(conn, path, origpkt, origpktlen); - if (rv == 0) { - return NGTCP2_ERR_DRAINING; - } - } - if (nread == NGTCP2_ERR_DISCARD_PKT) { - return 0; - } - return (int)nread; - } - - assert(pktlen >= (size_t)nread); - pkt += nread; - pktlen -= (size_t)nread; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "read packet %td left %zu", nread, pktlen); - } - - return 0; -} - -/* - * conn_is_retired_path returns nonzero if |path| is included in - * retired path list. - */ -static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) { - size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired); - ngtcp2_dcid *dcid; - - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); - if (ngtcp2_path_eq(&dcid->ps.path, path)) { - return 1; - } - } - - return 0; -} - -/* - * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for - * transmission. - */ -static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_frame_chain *nfrc; - int rv; - - assert(conn->server); - - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE; - nfrc->next = pktns->tx.frq; - pktns->tx.frq = nfrc; - - return 0; -} - -/** - * @function - * - * `conn_read_handshake` performs QUIC cryptographic handshake by - * reading given data. |pkt| points to the buffer to read and - * |pktlen| is the length of the buffer. |path| is the network path. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: (TBD). - */ -static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts) { - int rv; - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - /* TODO Better to log something when we ignore input */ - return 0; - case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts); - if (rv < 0) { - return rv; - } - - if (conn->state == NGTCP2_CS_CLIENT_INITIAL) { - /* Retry packet was received */ - return 0; - } - - assert(conn->hs_pktns); - - if (conn->hs_pktns->crypto.rx.ckm && conn->in_pktns) { - rv = conn_process_buffered_handshake_pkt(conn, ts); - if (rv != 0) { - return rv; - } - } - - return 0; - case NGTCP2_CS_SERVER_INITIAL: - rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts); - if (rv < 0) { - return rv; - } - - /* - * Client ServerHello might not fit into single Initial packet - * (e.g., resuming session with client authentication). If we get - * Client Initial which does not increase offset or it is 0RTT - * packet buffered, perform address validation in order to buffer - * validated data only. - */ - if (ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0) { - if (conn->in_pktns->crypto.strm.rx.rob && - ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob)) { - /* Address has been validated with token */ - if (conn->local.settings.token.len) { - return 0; - } - return NGTCP2_ERR_RETRY; - } - if (conn->in_pktns->rx.buffed_pkts) { - /* 0RTT is buffered, force retry */ - return NGTCP2_ERR_RETRY; - } - /* If neither CRYPTO frame nor 0RTT packet is processed, just - drop connection. */ - return NGTCP2_ERR_PROTO; - } - - /* Process re-ordered 0-RTT packets which arrived before Initial - packet. */ - if (conn->early.ckm) { - assert(conn->in_pktns); - - rv = conn_process_buffered_protected_pkt(conn, conn->in_pktns, ts); - if (rv != 0) { - return rv; - } - } - - return 0; - case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts); - if (rv < 0) { - return rv; - } - - if (conn->hs_pktns->crypto.rx.ckm) { - rv = conn_process_buffered_handshake_pkt(conn, ts); - if (rv != 0) { - return rv; - } - } - - if (conn->hs_pktns->rx.max_pkt_num != -1) { - conn_discard_initial_state(conn, ts); - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - /* If server hits amplification limit, it cancels loss detection - timer. If server receives a packet from client, the limit is - increased and server can send more. If server has - ack-eliciting Initial or Handshake packets, it should resend - it if timer fired but timer is not armed in this case. So - instead of resending Initial/Handshake packets, if server has - 1RTT data to send, it might send them and then might hit - amplification limit again until it hits stream data limit. - Initial/Handshake data is not resent. In order to avoid this - situation, try to arm loss detection and check the expiry - here so that on next write call, we can resend - Initial/Handshake first. */ - if (!conn->cstat.loss_detection_timer) { - ngtcp2_conn_set_loss_detection_timer(conn, ts); - if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { - rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); - if (rv != 0) { - return rv; - } - } - } - - return 0; - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { - return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; - } - - rv = conn_handshake_completed(conn); - if (rv != 0) { - return rv; - } - conn->state = NGTCP2_CS_POST_HANDSHAKE; - - rv = conn_call_activate_dcid(conn, &conn->dcid.current); - if (rv != 0) { - return rv; - } - - rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); - if (rv != 0) { - return rv; - } - - conn_discard_handshake_state(conn, ts); - - rv = conn_enqueue_handshake_done(conn); - if (rv != 0) { - return rv; - } - - conn->pktns.rtb.persistent_congestion_start_ts = ts; - - /* Re-arm loss detection timer here after handshake has been - confirmed. */ - ngtcp2_conn_set_loss_detection_timer(conn, ts); - - return 0; - case NGTCP2_CS_CLOSING: - return NGTCP2_ERR_CLOSING; - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_DRAINING; - default: - return 0; - } -} - -int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) { - int rv = 0; - - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu", - pktlen); - - if (pktlen == 0) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - /* client does not expect a packet from unknown path. */ - if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && - (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)) && - !conn_is_retired_path(conn, path)) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "ignore packet from unknown path"); - return 0; - } - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: - case NGTCP2_CS_SERVER_INITIAL: - case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: - return conn_read_handshake(conn, path, pkt, pktlen, ts); - case NGTCP2_CS_CLOSING: - return NGTCP2_ERR_CLOSING; - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_DRAINING; - case NGTCP2_CS_POST_HANDSHAKE: - rv = conn_prepare_key_update(conn, ts); - if (rv != 0) { - return rv; - } - return conn_recv_cpkt(conn, path, pkt, pktlen, ts); - default: - assert(0); - } -} - -/* - * conn_check_pkt_num_exhausted returns nonzero if packet number is - * exhausted in at least one of packet number space. - */ -static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) { - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_pktns *hs_pktns = conn->hs_pktns; - - return (in_pktns && in_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || - (hs_pktns && hs_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || - conn->pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM; -} - -/* - * conn_server_hs_tx_left returns the maximum number of bytes that - * server is allowed to send during handshake. - */ -static size_t conn_server_hs_tx_left(ngtcp2_conn *conn) { - if (conn->flags & NGTCP2_CONN_FLAG_SADDR_VERIFIED) { - return SIZE_MAX; - } - /* From QUIC spec: Prior to validating the client address, servers - MUST NOT send more than three times as many bytes as the number - of bytes they have received. */ - assert(conn->cstat.bytes_recv * 3 >= conn->cstat.bytes_sent); - - return conn->cstat.bytes_recv * 3 - conn->cstat.bytes_sent; -} - -/* - * conn_retransmit_retry_early retransmits 0RTT packet after Retry is - * received from server. - */ -static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn, - uint8_t *dest, size_t destlen, - ngtcp2_tstamp ts) { - return conn_write_pkt(conn, dest, destlen, NULL, NGTCP2_PKT_0RTT, - NGTCP2_WRITE_PKT_FLAG_NONE, ts); -} - -/* - * conn_handshake_probe_left returns nonzero if there are probe - * packets to be sent for Initial or Handshake packet number space - * left. - */ -static int conn_handshake_probe_left(ngtcp2_conn *conn) { - return (conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || - conn->hs_pktns->rtb.probe_pkt_left; -} - -/* - * conn_write_handshake writes QUIC handshake packets to the buffer - * pointed by |dest| of length |destlen|. |early_datalen| specifies - * the expected length of early data to send. Specify 0 to - * |early_datalen| if there is no early data. - * - * This function returns the number of bytes written to the buffer, or - * one of the following negative error codes: - * - * NGTCP2_ERR_PKT_NUM_EXHAUSTED - * Packet number is exhausted. - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM - * Required transport parameter is missing. - * NGTCP2_CS_CLOSING - * Connection is in closing state. - * NGTCP2_CS_DRAINING - * Connection is in draining state. - * - * In addition to the above negative error codes, the same error codes - * from conn_recv_pkt may also be returned. - */ -static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, size_t early_datalen, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; - size_t origlen = destlen; - size_t server_hs_tx_left; - ngtcp2_conn_stat *cstat = &conn->cstat; - size_t pending_early_datalen; - ngtcp2_dcid *dcid; - ngtcp2_preferred_addr *paddr; - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - pending_early_datalen = conn_retry_early_payloadlen(conn); - if (pending_early_datalen) { - early_datalen = pending_early_datalen; - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) { - nwrite = - conn_write_client_initial(conn, dest, destlen, early_datalen, ts); - if (nwrite <= 0) { - return nwrite; - } - } else { - nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, - early_datalen, ts); - if (nwrite < 0) { - return nwrite; - } - } - - if (pending_early_datalen) { - early_spktlen = conn_retransmit_retry_early(conn, dest + nwrite, - destlen - (size_t)nwrite, ts); - - if (early_spktlen < 0) { - assert(ngtcp2_err_is_fatal((int)early_spktlen)); - return early_spktlen; - } - } - - conn->state = NGTCP2_CS_CLIENT_WAIT_HANDSHAKE; - - res = nwrite + early_spktlen; - cstat->bytes_sent += (size_t)res; - - return res; - case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - if (!conn_handshake_probe_left(conn) && conn_cwnd_is_zero(conn)) { - destlen = 0; - } else { - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { - pending_early_datalen = conn_retry_early_payloadlen(conn); - if (pending_early_datalen) { - early_datalen = pending_early_datalen; - } - } - - nwrite = - conn_write_handshake_pkts(conn, dest, destlen, early_datalen, ts); - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - nwrite = conn_retransmit_retry_early(conn, dest, destlen, ts); - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - - if (res == 0) { - nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); - if (nwrite < 0) { - return nwrite; - } - res = nwrite; - } - - cstat->bytes_sent += (size_t)res; - - return res; - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { - return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; - } - - rv = conn_handshake_completed(conn); - if (rv != 0) { - return (ngtcp2_ssize)rv; - } - - conn->state = NGTCP2_CS_POST_HANDSHAKE; - - if (conn->remote.transport_params.preferred_address_present) { - assert(!ngtcp2_ringbuf_full(&conn->dcid.unused)); - - paddr = &conn->remote.transport_params.preferred_address; - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); - ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); - - rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1); - if (rv != 0) { - return (ngtcp2_ssize)rv; - } - } - - if (conn->remote.transport_params.stateless_reset_token_present) { - assert(conn->dcid.current.seq == 0); - memcpy(conn->dcid.current.token, - conn->remote.transport_params.stateless_reset_token, - sizeof(conn->dcid.current.token)); - } - - rv = conn_call_activate_dcid(conn, &conn->dcid.current); - if (rv != 0) { - return rv; - } - - conn_process_early_rtb(conn); - - rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); - if (rv != 0) { - return (ngtcp2_ssize)rv; - } - - cstat->bytes_sent += (size_t)res; - - return res; - case NGTCP2_CS_SERVER_INITIAL: - nwrite = conn_write_handshake_pkts(conn, dest, destlen, - /* early_datalen = */ 0, ts); - if (nwrite < 0) { - return nwrite; - } - - if (nwrite) { - conn->state = NGTCP2_CS_SERVER_WAIT_HANDSHAKE; - cstat->bytes_sent += (size_t)nwrite; - } - - return nwrite; - case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - server_hs_tx_left = conn_server_hs_tx_left(conn); - if (server_hs_tx_left == 0) { - if (cstat->loss_detection_timer) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss detection timer canceled"); - cstat->loss_detection_timer = 0; - cstat->pto_count = 0; - } - return 0; - } - - if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) { - nwrite = conn_write_handshake_pkts(conn, dest, destlen, - /* early_datalen = */ 0, ts); - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - } - - if (res == 0) { - nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - dest += nwrite; - origlen -= (size_t)nwrite; - } - - cstat->bytes_sent += (size_t)res; - return res; - } - - return 0; - case NGTCP2_CS_CLOSING: - return NGTCP2_ERR_CLOSING; - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_DRAINING; - default: - return 0; - } -} - -/** - * @function - * - * `conn_client_write_handshake` writes client side handshake data and - * 0RTT packet. - * - * In order to send STREAM data in 0RTT packet, specify - * |vmsg|->stream. |vmsg|->stream.strm, |vmsg|->stream.fin, - * |vmsg|->stream.data, and |vmsg|->stream.datacnt are stream to which - * 0-RTT data is sent, whether it is a last data chunk in this stream, - * a vector of 0-RTT data, and its number of elements respectively. - * The amount of 0RTT data sent is assigned to - * *|vmsg|->stream.pdatalen. If no data is sent, -1 is assigned. - * Note that 0 length STREAM frame is allowed in QUIC, so 0 might be - * assigned to *|vmsg|->stream.pdatalen. - * - * 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. - * - * This function returns the number of bytes written to the buffer - * 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, - uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts) { - int send_stream = 0; - ngtcp2_ssize spktlen, early_spktlen; - int was_client_initial; - size_t datalen; - size_t early_datalen = 0; - uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; - int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; - - assert(!conn->server); - - /* conn->early.ckm might be created in the first call of - conn_handshake(). Check it later. */ - if (vmsg && vmsg->type == NGTCP2_VMSG_TYPE_STREAM && - !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { - datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); - send_stream = - conn_retry_early_payloadlen(conn) == 0 && - /* 0 length STREAM frame is allowed */ - (datalen == 0 || - (datalen > 0 && - (vmsg->stream.strm->tx.max_offset - vmsg->stream.strm->tx.offset) && - (conn->tx.max_offset - conn->tx.offset))); - if (send_stream) { - early_datalen = - conn_enforce_flow_control(conn, vmsg->stream.strm, datalen) + - NGTCP2_STREAM_OVERHEAD; - - if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { - wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; - } - } else { - vmsg = NULL; - } - } - - if (!ppe_pending) { - was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL; - spktlen = conn_write_handshake(conn, dest, destlen, early_datalen, ts); - - if (spktlen < 0) { - return spktlen; - } - - if (conn->pktns.crypto.tx.ckm || !conn->early.ckm || !send_stream) { - return spktlen; - } - } else { - assert(!conn->pktns.crypto.tx.ckm); - assert(conn->early.ckm); - - was_client_initial = conn->pkt.was_client_initial; - spktlen = conn->pkt.hs_spktlen; - } - - /* If spktlen > 0, we are making a compound packet. If Initial - packet is written, we have to pad bytes to 0-RTT packet. */ - - if (spktlen && was_client_initial) { - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - } - - dest += spktlen; - destlen -= (size_t)spktlen; - - if (conn_cwnd_is_zero(conn)) { - return spktlen; - } - - early_spktlen = - conn_write_pkt(conn, dest, destlen, vmsg, NGTCP2_PKT_0RTT, wflags, ts); - - if (early_spktlen < 0) { - switch (early_spktlen) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: - return spktlen; - case NGTCP2_ERR_WRITE_MORE: - conn->pkt.was_client_initial = was_client_initial; - conn->pkt.hs_spktlen = spktlen; - break; - } - return early_spktlen; - } - - conn->cstat.bytes_sent += (size_t)early_spktlen; - - return spktlen + early_spktlen; -} - -void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) { - conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; - if (conn->server) { - conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; - } -} - -int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { - return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED); -} - -int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr, - int64_t pkt_num, int active_ack, ngtcp2_tstamp ts) { - int rv; - (void)conn; - - rv = ngtcp2_acktr_add(acktr, pkt_num, active_ack, ts); - if (rv != 0) { - assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); - return rv; - } - - return 0; -} - -int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) { - ngtcp2_ssize nread; - ngtcp2_pkt_hd hd, *p; - - if (dest) { - p = dest; - } else { - p = &hd; - } - - if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) { - return -1; - } - - nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen); - if (nread < 0) { - return -1; - } - - switch (p->type) { - case NGTCP2_PKT_INITIAL: - if (pktlen < NGTCP2_MIN_INITIAL_PKTLEN) { - return -1; - } - if (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { - return -1; - } - break; - case NGTCP2_PKT_0RTT: - /* 0-RTT packet may arrive before Initial packet due to - re-ordering. */ - break; - default: - return -1; - } - - switch (p->version) { - case NGTCP2_PROTO_VER: - break; - default: - return 1; - } - - return 0; -} - -void ngtcp2_conn_set_aead_overhead(ngtcp2_conn *conn, size_t aead_overhead) { - conn->crypto.aead_overhead = aead_overhead; -} - -size_t ngtcp2_conn_get_aead_overhead(ngtcp2_conn *conn) { - return conn->crypto.aead_overhead; -} - -int ngtcp2_conn_install_initial_key( - ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx, - const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, - const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, - const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) { - ngtcp2_pktns *pktns = conn->in_pktns; - int rv; - - assert(pktns); - - conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); - pktns->crypto.rx.hp_ctx.native_handle = NULL; - - if (pktns->crypto.rx.ckm) { - conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx); - ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); - pktns->crypto.rx.ckm = NULL; - } - - conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx); - pktns->crypto.tx.hp_ctx.native_handle = NULL; - - if (pktns->crypto.tx.ckm) { - conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); - ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); - pktns->crypto.tx.ckm = NULL; - } - - rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, NULL, rx_iv, ivlen, - conn->mem); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, NULL, tx_iv, ivlen, - conn->mem); - if (rv != 0) { - return rv; - } - - /* Take owner ship after we are sure that no failure occurs, so that - caller can delete these contexts on failure. */ - pktns->crypto.rx.ckm->aead_ctx = *rx_aead_ctx; - pktns->crypto.rx.hp_ctx = *rx_hp_ctx; - pktns->crypto.tx.ckm->aead_ctx = *tx_aead_ctx; - pktns->crypto.tx.hp_ctx = *tx_hp_ctx; - - return 0; -} - -int ngtcp2_conn_install_rx_handshake_key( - ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { - ngtcp2_pktns *pktns = conn->hs_pktns; - int rv; - - assert(pktns); - assert(!pktns->crypto.rx.hp_ctx.native_handle); - assert(!pktns->crypto.rx.ckm); - - rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, aead_ctx, iv, ivlen, - conn->mem); - if (rv != 0) { - return rv; - } - - pktns->crypto.rx.hp_ctx = *hp_ctx; - - return 0; -} - -int ngtcp2_conn_install_tx_handshake_key( - ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { - ngtcp2_pktns *pktns = conn->hs_pktns; - int rv; - - assert(pktns); - assert(!pktns->crypto.tx.hp_ctx.native_handle); - assert(!pktns->crypto.tx.ckm); - - rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, aead_ctx, iv, ivlen, - conn->mem); - if (rv != 0) { - return rv; - } - - pktns->crypto.tx.hp_ctx = *hp_ctx; - - if (conn->server) { - return ngtcp2_conn_commit_local_transport_params(conn); - } - - return 0; -} - -int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, - const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, - const ngtcp2_crypto_cipher_ctx *hp_ctx) { - int rv; - - assert(!conn->early.hp_ctx.native_handle); - assert(!conn->early.ckm); - - rv = ngtcp2_crypto_km_new(&conn->early.ckm, NULL, 0, aead_ctx, iv, ivlen, - conn->mem); - if (rv != 0) { - return rv; - } - - conn->early.hp_ctx = *hp_ctx; - - return 0; -} - -int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret, - size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, - const ngtcp2_crypto_cipher_ctx *hp_ctx) { - ngtcp2_pktns *pktns = &conn->pktns; - int rv; - - assert(!pktns->crypto.rx.hp_ctx.native_handle); - assert(!pktns->crypto.rx.ckm); - - rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, secret, secretlen, aead_ctx, - iv, ivlen, conn->mem); - if (rv != 0) { - return rv; - } - - pktns->crypto.rx.hp_ctx = *hp_ctx; - - return 0; -} - -int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, - size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, - const ngtcp2_crypto_cipher_ctx *hp_ctx) { - ngtcp2_pktns *pktns = &conn->pktns; - int rv; - - assert(!pktns->crypto.tx.hp_ctx.native_handle); - assert(!pktns->crypto.tx.ckm); - - rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, secret, secretlen, aead_ctx, - iv, ivlen, conn->mem); - if (rv != 0) { - return rv; - } - - pktns->crypto.tx.hp_ctx = *hp_ctx; - - conn->remote.transport_params = conn->remote.pending_transport_params; - conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; - - return 0; -} - -int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; - ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); - - assert(conn->state == NGTCP2_CS_POST_HANDSHAKE); - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || - (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || - !conn->crypto.key_update.new_tx_ckm || - !conn->crypto.key_update.new_rx_ckm || - (confirmed_ts != UINT64_MAX && confirmed_ts + 3 * pto > ts)) { - return NGTCP2_ERR_INVALID_STATE; - } - - conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM); - - return 0; -} - -ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn) { - if (conn->cstat.loss_detection_timer) { - return conn->cstat.loss_detection_timer; - } - return UINT64_MAX; -} - -ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { - ngtcp2_tstamp res = UINT64_MAX; - ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); - ngtcp2_scid *scid; - ngtcp2_dcid *dcid; - - if (conn->pv) { - res = ngtcp2_pv_next_expiry(conn->pv); - } - - if (!ngtcp2_pq_empty(&conn->scid.used)) { - scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); - if (scid->ts_retired != UINT64_MAX) { - res = ngtcp2_min(res, scid->ts_retired + pto); - } - } - - if (ngtcp2_ringbuf_len(&conn->dcid.retired)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); - res = ngtcp2_min(res, dcid->ts_retired + pto); - } - - return res; -} - -ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) { - ngtcp2_acktr *acktr = &conn->pktns.acktr; - - if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && - acktr->first_unacked_ts != UINT64_MAX) { - return acktr->first_unacked_ts + conn_compute_ack_delay(conn); - } - return UINT64_MAX; -} - -ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { - ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn); - ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn); - ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn); - ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn); - ngtcp2_tstamp res = ngtcp2_min(t1, t2); - res = ngtcp2_min(res, t3); - return ngtcp2_min(res, t4); -} - -int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - int rv; - - ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); - - ngtcp2_conn_remove_lost_pkt(conn, ts); - - if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { - rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); - if (rv != 0) { - return rv; - } - } - - return 0; -} - -static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, - ngtcp2_tstamp ts) { - if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && - acktr->first_unacked_ts <= ts) { - acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER; - } -} - -void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, - ngtcp2_tstamp ts) { - if (conn->in_pktns) { - acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts); - } - if (conn->hs_pktns) { - acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts); - } - acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); -} - -ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { - ngtcp2_tstamp res = UINT64_MAX, ts; - - 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); - res = ngtcp2_min(res, ts); - } - } - - 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); - res = ngtcp2_min(res, ts); - } - } - - ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb); - if (ts != UINT64_MAX) { - ts += conn_compute_pto(conn, &conn->pktns); - res = ngtcp2_min(res, ts); - } - - return res; -} - -void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_tstamp pto; - - if (conn->in_pktns) { - pto = conn_compute_pto(conn, conn->in_pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, 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); - } - pto = conn_compute_pto(conn, &conn->pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); -} - -/* - * conn_client_validate_transport_params validates |params| as client. - * |params| must be sent with Encrypted Extensions. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_PROTO - * Validation against either of original_dcid and retry_scid is - * failed. - * NGTCP2_ERR_TRANSPORT_PARAM - * params contains preferred address but server chose zero-length - * connection ID. - */ -static int -conn_client_validate_transport_params(ngtcp2_conn *conn, - const ngtcp2_transport_params *params) { - if (!ngtcp2_cid_eq(&conn->rcid, ¶ms->original_dcid)) { - return NGTCP2_ERR_TRANSPORT_PARAM; - } - - if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { - if (!params->retry_scid_present) { - return NGTCP2_ERR_TRANSPORT_PARAM; - } - if (!ngtcp2_cid_eq(&conn->retry_scid, ¶ms->retry_scid)) { - return NGTCP2_ERR_TRANSPORT_PARAM; - } - } else if (params->retry_scid_present) { - return NGTCP2_ERR_TRANSPORT_PARAM; - } - - if (params->preferred_address_present && - conn->dcid.current.cid.datalen == 0) { - return NGTCP2_ERR_TRANSPORT_PARAM; - } - - return 0; -} - -int ngtcp2_conn_set_remote_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params) { - int rv; - - assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)); - - /* Assume that ngtcp2_decode_transport_params sets default value if - active_connection_id_limit is omitted. */ - if (params->active_connection_id_limit < - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { - return NGTCP2_ERR_TRANSPORT_PARAM; - } - - /* We assume that conn->dcid.current.cid is still the initial one. - This requires that transport parameter must be fed into - ngtcp2_conn as early as possible. */ - if (!ngtcp2_cid_eq(&conn->dcid.current.cid, ¶ms->initial_scid)) { - return NGTCP2_ERR_TRANSPORT_PARAM; - } - - if (!conn->server) { - rv = conn_client_validate_transport_params(conn, params); - if (rv != 0) { - return rv; - } - } - - ngtcp2_log_remote_tp(&conn->log, - conn->server - ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO - : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, - params); - - ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, - NGTCP2_QLOG_SIDE_REMOTE); - - if (conn->pktns.crypto.tx.ckm) { - conn->remote.transport_params = *params; - conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; - } else { - conn->remote.pending_transport_params = *params; - } - - conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED; - - return 0; -} - -void ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params) { - if (conn->pktns.crypto.rx.ckm) { - *params = conn->remote.transport_params; - } else { - *params = conn->remote.pending_transport_params; - } -} - -void ngtcp2_conn_set_early_remote_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params) { - ngtcp2_transport_params *p = &conn->remote.transport_params; - - assert(!conn->server); - - memset(p, 0, sizeof(*p)); - - p->initial_max_streams_bidi = params->initial_max_streams_bidi; - p->initial_max_streams_uni = params->initial_max_streams_uni; - p->initial_max_stream_data_bidi_local = - params->initial_max_stream_data_bidi_local; - p->initial_max_stream_data_bidi_remote = - params->initial_max_stream_data_bidi_remote; - p->initial_max_stream_data_uni = params->initial_max_stream_data_uni; - p->initial_max_data = params->initial_max_data; - - conn_sync_stream_id_limit(conn); - - conn->tx.max_offset = p->initial_max_data; - - ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, p, conn->server, - NGTCP2_QLOG_SIDE_REMOTE); -} - -int ngtcp2_conn_set_local_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params) { - assert(conn->server); - assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); - - if (conn->hs_pktns == NULL || conn->hs_pktns->crypto.tx.ckm) { - return NGTCP2_ERR_INVALID_STATE; - } - - conn->local.settings.transport_params = *params; - - return 0; -} - -int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { - const ngtcp2_mem *mem = conn->mem; - ngtcp2_transport_params *params = &conn->local.settings.transport_params; - ngtcp2_scid *scident; - ngtcp2_ksl_it it; - int rv; - - assert(1 == ngtcp2_ksl_len(&conn->scid.set)); - - if (params->active_connection_id_limit == 0) { - params->active_connection_id_limit = - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; - } - - params->initial_scid = conn->oscid; - - if (conn->oscid.datalen == 0) { - params->preferred_address_present = 0; - } - - if (conn->server) { - if (params->stateless_reset_token_present) { - it = ngtcp2_ksl_begin(&conn->scid.set); - scident = ngtcp2_ksl_it_get(&it); - - memcpy(scident->token, params->stateless_reset_token, - NGTCP2_STATELESS_RESET_TOKENLEN); - } - - if (params->preferred_address_present) { - scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); - if (scident == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid, - params->preferred_address.stateless_reset_token); - - rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident); - if (rv != 0) { - ngtcp2_mem_free(mem, scident); - return rv; - } - - conn->scid.last_seq = 1; - } - } - - conn->rx.unsent_max_offset = conn->rx.max_offset = params->initial_max_data; - conn->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi; - conn->remote.bidi.max_streams = params->initial_max_streams_bidi; - conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni; - conn->remote.uni.max_streams = params->initial_max_streams_uni; - - ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, - NGTCP2_QLOG_SIDE_LOCAL); - - return 0; -} - -void ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params) { - *params = conn->local.settings.transport_params; -} - -int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, - void *stream_user_data) { - int rv; - ngtcp2_strm *strm; - - if (ngtcp2_conn_get_streams_bidi_left(conn) == 0) { - return NGTCP2_ERR_STREAM_ID_BLOCKED; - } - - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); - if (strm == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id, - stream_user_data); - if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); - return rv; - } - - *pstream_id = conn->local.bidi.next_stream_id; - conn->local.bidi.next_stream_id += 4; - - return 0; -} - -int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, - void *stream_user_data) { - int rv; - ngtcp2_strm *strm; - - if (ngtcp2_conn_get_streams_uni_left(conn) == 0) { - return NGTCP2_ERR_STREAM_ID_BLOCKED; - } - - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); - if (strm == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id, - stream_user_data); - if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); - return rv; - } - ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); - - *pstream_id = conn->local.uni.next_stream_id; - conn->local.uni.next_stream_id += 4; - - return 0; -} - -ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) { - ngtcp2_map_entry *me; - - me = ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); - if (me == NULL) { - return NULL; - } - - return ngtcp2_struct_of(me, ngtcp2_strm, me); -} - -ngtcp2_ssize ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path, - 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; - - datav.len = datalen; - datav.base = (uint8_t *)data; - - return ngtcp2_conn_writev_stream(conn, path, dest, destlen, pdatalen, flags, - stream_id, &datav, 1, ts); -} - -ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, - uint8_t *dest, size_t destlen, - ngtcp2_ssize *pdatalen, uint32_t flags, - int64_t stream_id, - const ngtcp2_vec *datav, size_t datavcnt, - ngtcp2_tstamp ts) { - ngtcp2_vmsg vmsg, *pvmsg; - ngtcp2_strm *strm; - - if (pdatalen) { - *pdatalen = -1; - } - - if (stream_id != -1) { - strm = ngtcp2_conn_find_stream(conn, stream_id); - if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; - } - - if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { - return NGTCP2_ERR_STREAM_SHUT_WR; - } - - vmsg.type = NGTCP2_VMSG_TYPE_STREAM; - vmsg.stream.strm = strm; - vmsg.stream.flags = flags; - vmsg.stream.data = datav; - vmsg.stream.datacnt = datavcnt; - vmsg.stream.pdatalen = pdatalen; - - pvmsg = &vmsg; - } else { - pvmsg = NULL; - } - - return ngtcp2_conn_write_vmsg(conn, path, dest, destlen, pvmsg, ts); -} - -ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, - uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { - ngtcp2_ssize nwrite; - ngtcp2_pktns *pktns = &conn->pktns; - size_t origlen = destlen; - int rv; - uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; - int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; - ngtcp2_ssize res = 0; - size_t server_hs_tx_left; - - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; - - if (path) { - ngtcp2_path_copy(path, &conn->dcid.current.ps.path); - } - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: - nwrite = conn_client_write_handshake(conn, dest, destlen, vmsg, ts); - if (nwrite < 0 || conn->state != NGTCP2_CS_POST_HANDSHAKE) { - return nwrite; - } - res = nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - /* Break here so that we can coalesces Short packets. */ - break; - case NGTCP2_CS_SERVER_INITIAL: - case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: - if (!ppe_pending) { - server_hs_tx_left = conn_server_hs_tx_left(conn); - destlen = ngtcp2_min(destlen, server_hs_tx_left); - - nwrite = conn_write_handshake(conn, dest, destlen, 0, ts); - if (nwrite < 0) { - return nwrite; - } - - if (conn->flags & NGTCP2_CONN_FLAG_SADDR_VERIFIED) { - destlen = origlen; - } else { - origlen = destlen; - } - - res = nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - } - if (conn->state != NGTCP2_CS_POST_HANDSHAKE && - conn->pktns.crypto.tx.ckm == NULL) { - return res; - } - break; - case NGTCP2_CS_POST_HANDSHAKE: - break; - case NGTCP2_CS_CLOSING: - return NGTCP2_ERR_CLOSING; - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_DRAINING; - default: - return 0; - } - - assert(pktns->crypto.tx.ckm); - - if (conn_check_pkt_num_exhausted(conn)) { - return NGTCP2_ERR_PKT_NUM_EXHAUSTED; - } - - rv = conn_remove_retired_connection_id(conn, ts); - if (rv != 0) { - return rv; - } - - if (vmsg) { - switch (vmsg->type) { - case NGTCP2_VMSG_TYPE_STREAM: - if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { - wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; - } - break; - default: - break; - } - } - - if (ppe_pending) { - res = conn->pkt.hs_spktlen; - conn->pkt.hs_spktlen = 0; - /* 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, dest, destlen, vmsg, NGTCP2_PKT_SHORT, wflags, ts); - goto fin; - } else { - if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { - rv = conn_prepare_key_update(conn, ts); - if (rv != 0) { - return rv; - } - } - - if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) { - destlen = 0; - } else if (conn->pv) { - nwrite = conn_write_path_challenge(conn, path, dest, destlen, ts); - if (nwrite) { - goto fin; - } - } - } - - if (res == 0) { - if (conn_handshake_remnants_left(conn)) { - if (conn_handshake_probe_left(conn)) { - destlen = origlen; - } - nwrite = conn_write_handshake_pkts(conn, dest, destlen, 0, ts); - if (nwrite < 0) { - return nwrite; - } - if (nwrite > 0) { - res = nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - } - } - } - - if (conn->pktns.rtb.probe_pkt_left) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "transmit probe pkt left=%zu", - conn->pktns.rtb.probe_pkt_left); - - nwrite = - conn_write_pkt(conn, dest, destlen, vmsg, NGTCP2_PKT_SHORT, wflags, ts); - - goto fin; - } - - nwrite = - conn_write_pkt(conn, dest, destlen, vmsg, NGTCP2_PKT_SHORT, wflags, ts); - if (nwrite) { - assert(nwrite != NGTCP2_ERR_NOBUF); - goto fin; - } - - if (res == 0) { - nwrite = conn_write_ack_pkt(conn, dest, origlen, NGTCP2_PKT_SHORT, ts); - } - -fin: - conn->pkt.hs_spktlen = 0; - - if (nwrite >= 0) { - conn->cstat.bytes_sent += (size_t)nwrite; - return res + nwrite; - } - /* NGTCP2_CONN_FLAG_PPE_PENDING is set in conn_write_pkt above. - ppe_pending cannot be used here. */ - if (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) { - conn->pkt.hs_spktlen = res; - } - - return nwrite; -} - -static ngtcp2_ssize conn_write_connection_close(ngtcp2_conn *conn, - uint8_t *dest, size_t destlen, - uint8_t pkt_type, - uint64_t error_code, - ngtcp2_tstamp ts) { - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_pktns *hs_pktns = conn->hs_pktns; - ngtcp2_ssize res = 0, nwrite; - ngtcp2_frame fr; - - fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; - fr.connection_close.error_code = error_code; - fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = 0; - fr.connection_close.reason = NULL; - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && - pkt_type != NGTCP2_PKT_INITIAL) { - if (in_pktns && conn->server) { - nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid, &fr, - NGTCP2_RTB_FLAG_NONE, ts); - if (nwrite < 0) { - return nwrite; - } - - dest += nwrite; - destlen -= (size_t)nwrite; - res += nwrite; - } - - if (pkt_type != NGTCP2_PKT_HANDSHAKE && hs_pktns && - hs_pktns->crypto.tx.ckm) { - nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, dest, destlen, NGTCP2_PKT_HANDSHAKE, &conn->dcid.current.cid, - &fr, NGTCP2_RTB_FLAG_NONE, ts); - if (nwrite < 0) { - return nwrite; - } - - dest += nwrite; - destlen -= (size_t)nwrite; - res += nwrite; - } - } - - nwrite = ngtcp2_conn_write_single_frame_pkt(conn, dest, destlen, pkt_type, - &conn->dcid.current.cid, &fr, - NGTCP2_RTB_FLAG_NONE, ts); - - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - - if (res == 0) { - return NGTCP2_ERR_NOBUF; - } - - return res; -} - -ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, - ngtcp2_path *path, - uint8_t *dest, size_t destlen, - uint64_t error_code, - ngtcp2_tstamp ts) { - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_pktns *hs_pktns = conn->hs_pktns; - uint8_t pkt_type; - ngtcp2_ssize nwrite; - - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; - - if (conn_check_pkt_num_exhausted(conn)) { - return NGTCP2_ERR_PKT_NUM_EXHAUSTED; - } - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - case NGTCP2_CS_CLOSING: - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; - default: - break; - } - - if (path) { - ngtcp2_path_copy(path, &conn->dcid.current.ps.path); - } - - if (conn->state == NGTCP2_CS_POST_HANDSHAKE || - (conn->server && conn->pktns.crypto.tx.ckm)) { - pkt_type = NGTCP2_PKT_SHORT; - } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { - pkt_type = NGTCP2_PKT_HANDSHAKE; - } else if (in_pktns && in_pktns->crypto.tx.ckm) { - pkt_type = NGTCP2_PKT_INITIAL; - } else { - /* This branch is taken if server has not read any Initial packet - from client. */ - return NGTCP2_ERR_INVALID_STATE; - } - - nwrite = conn_write_connection_close(conn, dest, destlen, pkt_type, - error_code, ts); - if (nwrite < 0) { - return nwrite; - } - - conn->state = NGTCP2_CS_CLOSING; - - return nwrite; -} - -ngtcp2_ssize ngtcp2_conn_write_application_close(ngtcp2_conn *conn, - ngtcp2_path *path, - uint8_t *dest, size_t destlen, - uint64_t app_error_code, - ngtcp2_tstamp ts) { - ngtcp2_ssize nwrite; - ngtcp2_ssize res = 0; - ngtcp2_frame fr; - - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; - - if (conn_check_pkt_num_exhausted(conn)) { - return NGTCP2_ERR_PKT_NUM_EXHAUSTED; - } - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - case NGTCP2_CS_CLOSING: - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; - default: - break; - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { - nwrite = conn_write_connection_close(conn, dest, destlen, - conn->hs_pktns->crypto.tx.ckm - ? NGTCP2_PKT_HANDSHAKE - : NGTCP2_PKT_INITIAL, - NGTCP2_APPLICATION_ERROR, ts); - if (nwrite < 0) { - return nwrite; - } - res = nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; - } - - if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { - assert(res); - - if (!conn->server || !conn->pktns.crypto.tx.ckm) { - return res; - } - } - - assert(conn->pktns.crypto.tx.ckm); - - if (path) { - ngtcp2_path_copy(path, &conn->dcid.current.ps.path); - } - - fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP; - fr.connection_close.error_code = app_error_code; - fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = 0; - fr.connection_close.reason = NULL; - - nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr, - NGTCP2_RTB_FLAG_NONE, ts); - - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - - if (res == 0) { - return NGTCP2_ERR_NOBUF; - } - - conn->state = NGTCP2_CS_CLOSING; - - return res; -} - -int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) { - return conn->state == NGTCP2_CS_CLOSING; -} - -int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn) { - return conn->state == NGTCP2_CS_DRAINING; -} - -int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { - int rv; - - if (!strm->app_error_code) { - app_error_code = strm->app_error_code; - } - - rv = ngtcp2_map_remove(&conn->strms, strm->me.key); - if (rv != 0) { - assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); - return rv; - } - - rv = conn_call_stream_close(conn, strm, app_error_code); - if (rv != 0) { - goto fin; - } - - if (ngtcp2_strm_is_tx_queued(strm)) { - ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe); - } - -fin: - ngtcp2_strm_free(strm); - ngtcp2_mem_free(conn->mem, strm); - - return rv; -} - -int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) == - NGTCP2_STRM_FLAG_SHUT_RDWR && - ((strm->flags & NGTCP2_STRM_FLAG_RECV_RST) || - ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) && - (((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && - (strm->flags & NGTCP2_STRM_FLAG_RST_ACKED)) || - (!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && - ngtcp2_strm_is_all_tx_data_acked(strm)))) { - return ngtcp2_conn_close_stream(conn, strm, app_error_code); - } - return 0; -} - -/* - * conn_shutdown_stream_write closes send stream with error code - * |app_error_code|. RESET_STREAM frame is scheduled. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { - if (strm->flags & NGTCP2_STRM_FLAG_SENT_RST) { - return 0; - } - - /* Set this flag so that we don't accidentally send DATA to this - stream. */ - strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; - strm->app_error_code = app_error_code; - - ngtcp2_strm_streamfrq_clear(strm); - - return conn_reset_stream(conn, strm, app_error_code); -} - -/* - * conn_shutdown_stream_read closes read stream with error code - * |app_error_code|. STOP_SENDING frame is scheduled. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { - if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { - return 0; - } - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && - ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) { - return 0; - } - - /* Extend connection flow control window for the amount of data - which are not passed to application. */ - if (!(strm->flags & - (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST))) { - ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - - ngtcp2_strm_rx_offset(strm)); - } - - strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING; - strm->app_error_code = app_error_code; - - return conn_stop_sending(conn, strm, app_error_code); -} - -int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id, - uint64_t app_error_code) { - int rv; - ngtcp2_strm *strm; - - strm = ngtcp2_conn_find_stream(conn, stream_id); - if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; - } - - rv = conn_shutdown_stream_read(conn, strm, app_error_code); - if (rv != 0) { - return rv; - } - - rv = conn_shutdown_stream_write(conn, strm, app_error_code); - if (rv != 0) { - return rv; - } - - return 0; -} - -int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id, - uint64_t app_error_code) { - ngtcp2_strm *strm; - - strm = ngtcp2_conn_find_stream(conn, stream_id); - if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; - } - - return conn_shutdown_stream_write(conn, strm, app_error_code); -} - -int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, - uint64_t app_error_code) { - ngtcp2_strm *strm; - - strm = ngtcp2_conn_find_stream(conn, stream_id); - if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; - } - - return conn_shutdown_stream_read(conn, strm, app_error_code); -} - -/* - * conn_extend_max_stream_offset extends stream level flow control - * window by |datalen| of the stream denoted by |strm|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t datalen) { - ngtcp2_strm *top; - - if (datalen > NGTCP2_MAX_VARINT || - strm->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { - strm->rx.unsent_max_offset = NGTCP2_MAX_VARINT; - } else { - strm->rx.unsent_max_offset += datalen; - } - - if (!(strm->flags & - (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)) && - !ngtcp2_strm_is_tx_queued(strm) && - conn_should_send_max_stream_data(conn, strm)) { - if (!ngtcp2_pq_empty(&conn->tx.strmq)) { - top = ngtcp2_conn_tx_strmq_top(conn); - strm->cycle = top->cycle; - } - strm->cycle = conn_tx_strmq_first_cycle(conn); - return ngtcp2_conn_tx_strmq_push(conn, strm); - } - - return 0; -} - -int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, - uint64_t datalen) { - ngtcp2_strm *strm; - - strm = ngtcp2_conn_find_stream(conn, stream_id); - if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; - } - - return conn_extend_max_stream_offset(conn, strm, datalen); -} - -void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, uint64_t datalen) { - if (NGTCP2_MAX_VARINT < datalen || - conn->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { - conn->rx.unsent_max_offset = NGTCP2_MAX_VARINT; - return; - } - - conn->rx.unsent_max_offset += datalen; -} - -void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { - handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); -} - -void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { - handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); -} - -const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { - return &conn->dcid.current.cid; -} - -uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { - return conn->version; -} - -int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_rtb *rtb = &conn->pktns.rtb; - int rv; - - conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; - - rv = ngtcp2_rtb_remove_all(rtb, conn, pktns, &conn->cstat); - if (rv != 0) { - return rv; - } - - return rv; -} - -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay) { - ngtcp2_conn_stat *cstat = &conn->cstat; - - rtt = ngtcp2_max(rtt, NGTCP2_GRANULARITY); - - cstat->latest_rtt = rtt; - - if (cstat->min_rtt == UINT64_MAX) { - cstat->min_rtt = rtt; - cstat->smoothed_rtt = rtt; - cstat->rttvar = rtt / 2; - } else { - cstat->min_rtt = ngtcp2_min(cstat->min_rtt, rtt); - if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) { - ack_delay = - ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay); - } else { - ack_delay = ngtcp2_min(ack_delay, NGTCP2_DEFAULT_MAX_ACK_DELAY); - } - if (rtt > cstat->min_rtt + ack_delay) { - rtt -= ack_delay; - } - - cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt - ? rtt - cstat->smoothed_rtt - : cstat->smoothed_rtt - rtt)) / - 4; - cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8; - } - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 - " smoothed_rtt=%" PRIu64 " rttvar=%" PRIu64 - " ack_delay=%" PRIu64, - (uint64_t)(cstat->latest_rtt / NGTCP2_MILLISECONDS), - (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), - cstat->smoothed_rtt / NGTCP2_MILLISECONDS, - cstat->rttvar / NGTCP2_MILLISECONDS, - (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); -} - -void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) { - *cstat = conn->cstat; -} - -static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn, - ngtcp2_tstamp *pts, - const ngtcp2_tstamp *times) { - ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; - ngtcp2_pktns *res = ns[0]; - size_t i; - ngtcp2_tstamp earliest_ts = times[NGTCP2_PKTNS_ID_INITIAL]; - - for (i = NGTCP2_PKTNS_ID_HANDSHAKE; i < NGTCP2_PKTNS_ID_MAX; ++i) { - if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 || - (times[i] == UINT64_MAX || - (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) || - (i == NGTCP2_PKTNS_ID_APP && - !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { - continue; - } - - earliest_ts = times[i]; - res = ns[i]; - } - - if (res == NULL && !conn->server) { - earliest_ts = UINT64_MAX; - - if (conn->hs_pktns && conn->hs_pktns->crypto.tx.ckm) { - res = conn->hs_pktns; - } else { - res = conn->in_pktns; - } - } - - if (pts) { - *pts = earliest_ts; - } - return res; -} - -void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_conn_stat *cstat = &conn->cstat; - ngtcp2_duration timeout; - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_pktns *hs_pktns = conn->hs_pktns; - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_pktns *earliest_pktns; - ngtcp2_tstamp earliest_loss_time; - ngtcp2_tstamp last_tx_pkt_ts; - - conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); - - if (earliest_loss_time != UINT64_MAX) { - cstat->loss_detection_timer = earliest_loss_time; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss_detection_timer=%" PRIu64 " nonzero crypto loss time", - cstat->loss_detection_timer); - return; - } - - if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) && - (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) && - (pktns->rtb.num_retransmittable == 0 || - !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) && - (conn->server || - (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | - NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { - if (cstat->loss_detection_timer) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss detection timer canceled"); - cstat->loss_detection_timer = 0; - cstat->pto_count = 0; - } - return; - } - - earliest_pktns = - conn_get_earliest_pktns(conn, &last_tx_pkt_ts, cstat->last_tx_pkt_ts); - - assert(earliest_pktns); - - if (last_tx_pkt_ts == UINT64_MAX) { - last_tx_pkt_ts = ts; - } - - timeout = conn_compute_pto(conn, earliest_pktns) * (1ULL << cstat->pto_count); - - cstat->loss_detection_timer = last_tx_pkt_ts + timeout; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss_detection_timer=%" PRIu64 " last_tx_pkt_ts=%" PRIu64 - " timeout=%" PRIu64, - cstat->loss_detection_timer, last_tx_pkt_ts, - (uint64_t)(timeout / NGTCP2_MILLISECONDS)); -} - -int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_conn_stat *cstat = &conn->cstat; - int rv; - ngtcp2_pktns *in_pktns = conn->in_pktns; - ngtcp2_pktns *hs_pktns = conn->hs_pktns; - ngtcp2_tstamp earliest_loss_time; - ngtcp2_pktns *loss_pktns = - conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); - ngtcp2_pktns *earliest_pktns; - - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; - - switch (conn->state) { - case NGTCP2_CS_CLOSING: - case NGTCP2_CS_DRAINING: - cstat->loss_detection_timer = 0; - cstat->pto_count = 0; - return 0; - default: - break; - } - - if (!cstat->loss_detection_timer) { - return 0; - } - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss detection timer fired"); - - if (earliest_loss_time != UINT64_MAX) { - rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts); - if (rv != 0) { - return rv; - } - ngtcp2_conn_set_loss_detection_timer(conn, ts); - return 0; - } - - if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - if (hs_pktns->crypto.tx.ckm) { - hs_pktns->rtb.probe_pkt_left = 1; - } else { - in_pktns->rtb.probe_pkt_left = 1; - } - } else { - earliest_pktns = conn_get_earliest_pktns(conn, NULL, cstat->last_tx_pkt_ts); - - assert(earliest_pktns); - - switch (earliest_pktns->rtb.pktns_id) { - case NGTCP2_PKTNS_ID_INITIAL: - assert(in_pktns); - in_pktns->rtb.probe_pkt_left = 1; - if (!conn->server) { - break; - } - /* fall through for server so that it can coalesce packets. */ - /* fall through */ - case NGTCP2_PKTNS_ID_HANDSHAKE: - assert(hs_pktns); - hs_pktns->rtb.probe_pkt_left = 1; - break; - case NGTCP2_PKTNS_ID_APP: - conn->pktns.rtb.probe_pkt_left = 2; - break; - default: - assert(0); - } - } - - ++cstat->pto_count; - - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "pto_count=%zu", - cstat->pto_count); - - ngtcp2_conn_set_loss_detection_timer(conn, ts); - - return 0; -} - -int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - const uint8_t *data, const size_t datalen) { - ngtcp2_pktns *pktns; - ngtcp2_frame_chain *frc; - ngtcp2_crypto *fr; - int rv; - - if (datalen == 0) { - return 0; - } - - switch (crypto_level) { - case NGTCP2_CRYPTO_LEVEL_INITIAL: - assert(conn->in_pktns); - pktns = conn->in_pktns; - break; - case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - assert(conn->hs_pktns); - pktns = conn->hs_pktns; - break; - case NGTCP2_CRYPTO_LEVEL_APP: - pktns = &conn->pktns; - break; - default: - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - rv = ngtcp2_frame_chain_new(&frc, conn->mem); - if (rv != 0) { - return rv; - } - - fr = &frc->fr.crypto; - - fr->type = NGTCP2_FRAME_CRYPTO; - fr->offset = pktns->crypto.tx.offset; - fr->datacnt = 1; - fr->data[0].len = datalen; - fr->data[0].base = (uint8_t *)data; - - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc); - if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - - pktns->crypto.strm.tx.offset += datalen; - pktns->crypto.tx.offset += datalen; - - return 0; -} - -int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token, - size_t tokenlen) { - int rv; - ngtcp2_frame_chain *nfrc; - uint8_t *p; - - assert(conn->server); - assert(token); - assert(tokenlen); - - rv = ngtcp2_frame_chain_extralen_new(&nfrc, tokenlen, conn->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr.type = NGTCP2_FRAME_NEW_TOKEN; - - p = (uint8_t *)nfrc + sizeof(*nfrc); - memcpy(p, token, tokenlen); - - ngtcp2_vec_init(&nfrc->fr.new_token.token, p, tokenlen); - - nfrc->next = conn->pktns.tx.frq; - conn->pktns.tx.frq = nfrc; - - return 0; -} - -ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn) { - assert(!ngtcp2_pq_empty(&conn->tx.strmq)); - return ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); -} - -void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn) { - ngtcp2_strm *strm = ngtcp2_conn_tx_strmq_top(conn); - assert(strm); - ngtcp2_pq_pop(&conn->tx.strmq); - strm->pe.index = NGTCP2_PQ_BAD_INDEX; -} - -int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) { - return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe); -} - -size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) { - return ngtcp2_ksl_len(&conn->scid.set); -} - -size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { - ngtcp2_ksl_it it; - ngtcp2_scid *scid; - - for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - scid = ngtcp2_ksl_it_get(&it); - *dest++ = scid->cid; - } - - return ngtcp2_ksl_len(&conn->scid.set); -} - -size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { - size_t n = 1; /* for conn->dcid.current */ - ngtcp2_pv *pv = conn->pv; - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { - return 0; - } - - if (pv) { - if (pv->dcid.seq != conn->dcid.current.seq) { - ++n; - } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->fallback_dcid.seq != conn->dcid.current.seq && - pv->fallback_dcid.seq != pv->dcid.seq) { - ++n; - } - } - - n += ngtcp2_ringbuf_len(&conn->dcid.retired); - - return n; -} - -static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest, - const ngtcp2_dcid *src) { - dest->seq = src->seq; - dest->cid = src->cid; - ngtcp2_path_storage_init2(&dest->ps, &src->ps.path); - dest->token_present = - (uint8_t)!ngtcp2_check_invalid_stateless_reset_token(src->token); - memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); -} - -size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { - ngtcp2_pv *pv = conn->pv; - ngtcp2_cid_token *orig = dest; - ngtcp2_dcid *dcid; - size_t len, i; - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { - return 0; - } - - copy_dcid_to_cid_token(dest, &conn->dcid.current); - ++dest; - - if (pv) { - if (pv->dcid.seq != conn->dcid.current.seq) { - copy_dcid_to_cid_token(dest, &pv->dcid); - ++dest; - } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->fallback_dcid.seq != conn->dcid.current.seq && - pv->fallback_dcid.seq != pv->dcid.seq) { - copy_dcid_to_cid_token(dest, &pv->fallback_dcid); - ++dest; - } - } - - len = ngtcp2_ringbuf_len(&conn->dcid.retired); - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); - copy_dcid_to_cid_token(dest, dcid); - ++dest; - } - - return (size_t)(dest - orig); -} - -void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { - ngtcp2_addr *dest = &conn->dcid.current.ps.path.local; - - assert(addr->addrlen <= sizeof(conn->dcid.current.ps.local_addrbuf)); - ngtcp2_addr_copy(dest, addr); -} - -void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { - ngtcp2_addr *dest = &conn->dcid.current.ps.path.remote; - - assert(addr->addrlen <= sizeof(conn->dcid.current.ps.remote_addrbuf)); - ngtcp2_addr_copy(dest, addr); -} - -const ngtcp2_addr *ngtcp2_conn_get_remote_addr(ngtcp2_conn *conn) { - return &conn->dcid.current.ps.path.remote; -} - -int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_dcid *dcid; - - assert(!conn->server); - - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; - - if (conn->remote.transport_params.disable_active_migration || - !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { - return NGTCP2_ERR_INVALID_STATE; - } - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { - return NGTCP2_ERR_CONN_ID_BLOCKED; - } - - if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - - rv = conn_stop_pv(conn, ts); - if (rv != 0) { - return rv; - } - - rv = conn_retire_dcid(conn, &conn->dcid.current, ts); - if (rv != 0) { - return rv; - } - - ngtcp2_dcid_copy(&conn->dcid.current, dcid); - ngtcp2_path_copy(&conn->dcid.current.ps.path, path); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - - rv = conn_call_activate_dcid(conn, &conn->dcid.current); - if (rv != 0) { - return rv; - } - - conn_reset_congestion_state(conn); - - return 0; -} - -uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) { - return conn->local.uni.max_streams; -} - -uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { - return conn->tx.max_offset - conn->tx.offset; -} - -uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn) { - uint64_t n = ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id); - - return n > conn->local.bidi.max_streams - ? 0 - : conn->local.bidi.max_streams - n + 1; -} - -uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) { - uint64_t n = ngtcp2_ord_stream_id(conn->local.uni.next_stream_id); - - return n > conn->local.uni.max_streams ? 0 - : conn->local.uni.max_streams - n + 1; -} - -ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { - ngtcp2_duration trpto; - ngtcp2_duration idle_timeout; - - /* TODO Remote max_idle_timeout becomes effective after handshake - completion. */ - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || - conn->remote.transport_params.max_idle_timeout == 0 || - (conn->local.settings.transport_params.max_idle_timeout && - conn->local.settings.transport_params.max_idle_timeout < - conn->remote.transport_params.max_idle_timeout)) { - idle_timeout = conn->local.settings.transport_params.max_idle_timeout; - } else { - idle_timeout = conn->remote.transport_params.max_idle_timeout; - } - - if (idle_timeout == 0) { - return UINT64_MAX; - } - - trpto = 3 * conn_compute_pto( - conn, (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); - - return conn->idle_ts + ngtcp2_max(idle_timeout, trpto); -} - -ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { - return conn_compute_pto(conn, - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); -} - -void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, - const ngtcp2_crypto_ctx *ctx) { - assert(conn->in_pktns); - conn->in_pktns->crypto.ctx = *ctx; -} - -const ngtcp2_crypto_ctx *ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn) { - assert(conn->in_pktns); - return &conn->in_pktns->crypto.ctx; -} - -void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, - const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_aead_ctx *aead_ctx) { - assert(!conn->crypto.retry_aead_ctx.native_handle); - - conn->crypto.retry_aead = *aead; - conn->crypto.retry_aead_ctx = *aead_ctx; -} - -void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, - const ngtcp2_crypto_ctx *ctx) { - assert(conn->hs_pktns); - conn->hs_pktns->crypto.ctx = *ctx; - conn->pktns.crypto.ctx = *ctx; -} - -const ngtcp2_crypto_ctx *ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn) { - return &conn->pktns.crypto.ctx; -} - -void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn) { - return conn->crypto.tls_native_handle; -} - -void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, - void *tls_native_handle) { - conn->crypto.tls_native_handle = tls_native_handle; -} - -void ngtcp2_conn_get_connection_close_error_code( - ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec) { - *ccec = conn->rx.ccec; -} - -void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr) { - conn->crypto.tls_error = liberr; -} - -int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) { - return conn->crypto.tls_error; -} - -int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) { - return conn_local_stream(conn, stream_id); -} - -int ngtcp2_conn_is_server(ngtcp2_conn *conn) { return conn->server; } - -int ngtcp2_conn_after_retry(ngtcp2_conn *conn) { - return (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) != 0; -} - -int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, - void *stream_user_data) { - ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); - - if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; - } - - strm->stream_user_data = stream_user_data; - - return 0; -} - -void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, - const uint8_t *data) { - memcpy(pcent->data, data, sizeof(pcent->data)); -} - -void ngtcp2_settings_default(ngtcp2_settings *settings) { - memset(settings, 0, sizeof(*settings)); - settings->cc_algo = NGTCP2_CC_ALGO_CUBIC; - settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; - settings->transport_params.max_udp_payload_size = - NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; - settings->transport_params.ack_delay_exponent = - NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; - settings->transport_params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; - settings->transport_params.active_connection_id_limit = - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; -} - -/* The functions prefixed with ngtcp2_pkt_ are usually put inside - ngtcp2_pkt.c. This function uses encryption construct and uses - test data defined only in ngtcp2_conn_test.c, so it is written - here. */ -ngtcp2_ssize ngtcp2_pkt_write_connection_close( - uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, - const ngtcp2_crypto_cipher_ctx *hp_ctx) { - ngtcp2_pkt_hd hd; - ngtcp2_crypto_km ckm; - ngtcp2_crypto_cc cc; - ngtcp2_ppe ppe; - ngtcp2_frame fr = {0}; - int rv; - - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid, - scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, - NGTCP2_PROTO_VER, /* len = */ 0); - - ngtcp2_vec_init(&ckm.secret, NULL, 0); - ngtcp2_vec_init(&ckm.iv, iv, 12); - ckm.aead_ctx = *aead_ctx; - ckm.pkt_num = 0; - ckm.flags = NGTCP2_CRYPTO_KM_FLAG_NONE; - - cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; - cc.aead = *aead; - cc.hp = *hp; - cc.ckm = &ckm; - cc.hp_ctx = *hp_ctx; - cc.encrypt = encrypt; - cc.hp_mask = hp_mask; - - ngtcp2_ppe_init(&ppe, dest, destlen, &cc); - - rv = ngtcp2_ppe_encode_hd(&ppe, &hd); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return rv; - } - - if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { - return NGTCP2_ERR_NOBUF; - } - - fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; - fr.connection_close.error_code = error_code; - - rv = ngtcp2_ppe_encode_frame(&ppe, &fr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - return rv; - } - - return ngtcp2_ppe_final(&ppe, NULL); -} - -int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); } diff --git a/deps/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/lib/ngtcp2_conn.h deleted file mode 100644 index d7782b1114a..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_conn.h +++ /dev/null @@ -1,698 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_CONN_H -#define NGTCP2_CONN_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" -#include "ngtcp2_crypto.h" -#include "ngtcp2_acktr.h" -#include "ngtcp2_rtb.h" -#include "ngtcp2_strm.h" -#include "ngtcp2_mem.h" -#include "ngtcp2_idtr.h" -#include "ngtcp2_str.h" -#include "ngtcp2_pkt.h" -#include "ngtcp2_log.h" -#include "ngtcp2_pq.h" -#include "ngtcp2_cc.h" -#include "ngtcp2_pv.h" -#include "ngtcp2_cid.h" -#include "ngtcp2_buf.h" -#include "ngtcp2_ppe.h" -#include "ngtcp2_qlog.h" -#include "ngtcp2_rst.h" - -typedef enum { - /* Client specific handshake states */ - NGTCP2_CS_CLIENT_INITIAL, - NGTCP2_CS_CLIENT_WAIT_HANDSHAKE, - NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED, - /* Server specific handshake states */ - NGTCP2_CS_SERVER_INITIAL, - NGTCP2_CS_SERVER_WAIT_HANDSHAKE, - NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED, - /* Shared by both client and server */ - NGTCP2_CS_POST_HANDSHAKE, - NGTCP2_CS_CLOSING, - NGTCP2_CS_DRAINING, -} ngtcp2_conn_state; - -/* NGTCP2_MAX_STREAMS is the maximum number of streams. */ -#define NGTCP2_MAX_STREAMS (1LL << 60) - -/* NGTCP2_MAX_NUM_BUFFED_RX_PKTS is the maximum number of buffered - reordered packets. */ -#define NGTCP2_MAX_NUM_BUFFED_RX_PKTS 4 - -/* NGTCP2_MAX_REORDERED_CRYPTO_DATA is the maximum offset of crypto - data which is not continuous. In other words, there is a gap of - unreceived data. */ -#define NGTCP2_MAX_REORDERED_CRYPTO_DATA 65536 - -/* NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA is the maximum offset of received - crypto stream in Initial packet. We set this hard limit here - because crypto stream is unbounded. */ -#define NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA 65536 -/* NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA is the maximum offset of - received crypto stream in Handshake packet. We set this hard limit - here because crypto stream is unbounded. */ -#define NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA 65536 - -/* NGTCP2_MAX_RETRIES is the number of Retry packet which client can - accept. */ -#define NGTCP2_MAX_RETRIES 3 - -/* NGTCP2_MAX_DCID_POOL_SIZE is the maximum number of destination - connection ID the remote endpoint provides to store. It must be - the power of 2. */ -#define NGTCP2_MAX_DCID_POOL_SIZE 8 -/* NGTCP2_MAX_DCID_RETIRED_SIZE is the maximum number of retired DCID - kept to catch in-flight packet on retired path. */ -#define NGTCP2_MAX_DCID_RETIRED_SIZE 2 -/* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source - connection ID the local endpoint provides to the remote endpoint. - The chosen value was described in old draft. Now a remote endpoint - tells the maximum value. The value can be quite large, and we have - to put the sane limit.*/ -#define NGTCP2_MAX_SCID_POOL_SIZE 8 - -/* NGTCP2_MAX_NON_ACK_TX_PKT is the maximum number of continuous non - ACK-eliciting packets. */ -#define NGTCP2_MAX_NON_ACK_TX_PKT 3 - -/* - * ngtcp2_max_frame is defined so that it covers the largest ACK - * frame. - */ -typedef union { - ngtcp2_frame fr; - struct { - ngtcp2_ack ack; - /* ack includes 1 ngtcp2_ack_blk. */ - ngtcp2_ack_blk blks[NGTCP2_MAX_ACK_BLKS - 1]; - } ackfr; -} ngtcp2_max_frame; - -typedef struct { - uint8_t data[8]; -} ngtcp2_path_challenge_entry; - -void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, - const uint8_t *data); - -typedef enum { - NGTCP2_CONN_FLAG_NONE = 0x00, - /* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake - completed. */ - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED = 0x01, - /* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is - negotiated. This is only used for client. */ - NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED = 0x02, - /* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport - parameters are received. */ - NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED = 0x04, - /* NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT is set when a protected - packet is received, and decrypted successfully. This flag is - used to stop retransmitting handshake packets. It might be - replaced with an another mechanism when we implement key - update. */ - NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT = 0x08, - /* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry - packet. */ - NGTCP2_CONN_FLAG_RECV_RETRY = 0x10, - /* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is - rejected by a peer. */ - NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED = 0x20, - /* NGTCP2_CONN_FLAG_SADDR_VERIFIED is set when source address is - verified. */ - NGTCP2_CONN_FLAG_SADDR_VERIFIED = 0x40, - /* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint - confirmed completion of handshake. */ - NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED = 0x80, - /* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the - library transitions its state to "post handshake". */ - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED = 0x0100, - /* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update - is not confirmed by the local endpoint. That is, it has not - received ACK frame which acknowledges packet which is encrypted - with new key. */ - NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED = 0x0800, - /* NGTCP2_CONN_FLAG_PPE_PENDING is set when - NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state - of ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */ - NGTCP2_CONN_FLAG_PPE_PENDING = 0x1000, - /* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle - timer should be restarted on next write. */ - NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE = 0x2000, - /* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as - peer verified client address. This flag is only used by - client. */ - NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED = 0x4000, -} ngtcp2_conn_flag; - -typedef struct { - ngtcp2_buf buf; - /* pkt_type is the type of packet to send data in buf. If it is 0, - it must be sent in Short packet. Otherwise, it is sent the long - packet type denoted by pkt_type. */ - uint8_t pkt_type; -} ngtcp2_crypto_data; - -typedef struct ngtcp2_pktns { - struct { - /* last_pkt_num is the packet number which the local endpoint sent - last time.*/ - int64_t last_pkt_num; - ngtcp2_frame_chain *frq; - /* num_non_ack_pkt is the number of continuous non ACK-eliciting - packets. */ - size_t num_non_ack_pkt; - } tx; - - struct { - /* pngap tracks received packet number in order to suppress - duplicated packet number. */ - ngtcp2_gaptr pngap; - /* max_pkt_num is the largest packet number received so far. */ - int64_t max_pkt_num; - /* max_pkt_ts is the timestamp when max_pkt_num packet is - received. */ - ngtcp2_tstamp max_pkt_ts; - /* - * buffed_pkts is buffered packets which cannot be decrypted with - * the current encryption level. - * - * In server Initial encryption level, 0-RTT packet may be buffered. - * In server Handshake encryption level, Short packet may be buffered. - * - * In client Initial encryption level, Handshake or Short packet may - * be buffered. In client Handshake encryption level, Short packet - * may be buffered. - * - * - 0-RTT packet is only buffered in server Initial encryption - * level ngtcp2_pktns. - * - * - Handshake packet is only buffered in client Handshake - * encryption level ngtcp2_pktns. - * - * - Short packet is only buffered in Short encryption level - * ngtcp2_pktns. - */ - ngtcp2_pkt_chain *buffed_pkts; - } rx; - - struct { - struct { - /* frq contains crypto data sorted by their offset. */ - ngtcp2_ksl frq; - /* offset is the offset of crypto stream in this packet number - space. */ - uint64_t offset; - /* ckm is a cryptographic key, and iv to encrypt outgoing - packets. */ - ngtcp2_crypto_km *ckm; - /* hp_ctx is cipher context for packet header protection. */ - ngtcp2_crypto_cipher_ctx hp_ctx; - } tx; - - struct { - /* ckm is a cryptographic key, and iv to decrypt incoming - packets. */ - ngtcp2_crypto_km *ckm; - /* hp_ctx is cipher context for packet header protection. */ - ngtcp2_crypto_cipher_ctx hp_ctx; - } rx; - - ngtcp2_strm strm; - ngtcp2_crypto_ctx ctx; - } crypto; - - ngtcp2_acktr acktr; - ngtcp2_rtb rtb; -} ngtcp2_pktns; - -struct ngtcp2_conn { - ngtcp2_conn_state state; - ngtcp2_conn_callbacks callbacks; - /* rcid is a connection ID present in Initial or 0-RTT packet from - client as destination connection ID. Server uses this field to - check that duplicated Initial or 0-RTT packet are indeed sent to - this connection. It is also sent to client as - original_destination_connection_id transport parameter. Client - uses this field to validate original_destination_connection_id - transport parameter if no Retry packet is involved. */ - ngtcp2_cid rcid; - /* oscid is the source connection ID initially used by the local - endpoint. */ - ngtcp2_cid oscid; - /* retry_scid is the source connection ID from Retry packet. Client - records it in order to verify retry_source_connection_id - transport parameter. Server does not use this field. */ - ngtcp2_cid retry_scid; - ngtcp2_pktns *in_pktns; - ngtcp2_pktns *hs_pktns; - ngtcp2_pktns pktns; - - struct { - /* current is the current destination connection ID. */ - ngtcp2_dcid current; - /* unused is a set of unused CID received from peer. */ - ngtcp2_ringbuf unused; - /* retired is a set of CID retired by local endpoint. Keep them - in 3*PTO to catch packets in flight along the old path. */ - ngtcp2_ringbuf retired; - /* seqgap tracks received sequence numbers in order to ignore - retransmitted duplicated NEW_CONNECTION_ID frame. */ - ngtcp2_gaptr seqgap; - /* retire_prior_to is the largest retire_prior_to received so - far. */ - uint64_t retire_prior_to; - /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames - queued for transmission. */ - size_t num_retire_queued; - } dcid; - - struct { - /* set is a set of CID sent to peer. The peer can use any CIDs in - this set. This includes used CID as well as unused ones. */ - ngtcp2_ksl set; - /* used is a set of CID used by peer. The sort function of this - priority queue takes timestamp when CID is retired and sorts - them in ascending order. */ - ngtcp2_pq used; - /* last_seq is the last sequence number of connection ID. */ - uint64_t last_seq; - /* num_retired is the number of retired Connection ID still - included in set. */ - size_t num_retired; - } scid; - - struct { - /* strmq contains ngtcp2_strm which has frames to send. */ - ngtcp2_pq strmq; - /* ack is ACK frame. The underlying buffer is resused. */ - ngtcp2_frame *ack; - /* max_ack_blks is the number of additional ngtcp2_ack_blk which - ack can contain. */ - size_t max_ack_blks; - /* offset is the offset the local endpoint has sent to the remote - endpoint. */ - uint64_t offset; - /* max_offset is the maximum offset that local endpoint can - send. */ - uint64_t max_offset; - } tx; - - struct { - /* unsent_max_offset is the maximum offset that remote endpoint - can send without extending MAX_DATA. This limit is not yet - notified to the remote endpoint. */ - uint64_t unsent_max_offset; - /* offset is the cumulative sum of stream data received for this - connection. */ - uint64_t offset; - /* max_offset is the maximum offset that remote endpoint can - send. */ - uint64_t max_offset; - /* path_challenge stores received PATH_CHALLENGE data. */ - ngtcp2_ringbuf path_challenge; - /* ccec is the received connection close error code. */ - ngtcp2_connection_close_error_code ccec; - struct { - /* start_ts is the time instant when receiving rate measurement - is started. */ - ngtcp2_tstamp start_ts; - /* received is the number of bytes received in the current - measurement period. */ - uint64_t received; - } rate; - } rx; - - struct { - ngtcp2_crypto_km *ckm; - ngtcp2_crypto_cipher_ctx hp_ctx; - } early; - - struct { - ngtcp2_settings settings; - struct { - /* max_streams is the maximum number of bidirectional streams which - the local endpoint can open. */ - uint64_t max_streams; - /* next_stream_id is the bidirectional stream ID which the local - endpoint opens next. */ - int64_t next_stream_id; - } bidi; - - struct { - /* max_streams is the maximum number of unidirectional streams - which the local endpoint can open. */ - uint64_t max_streams; - /* next_stream_id is the unidirectional stream ID which the - local endpoint opens next. */ - int64_t next_stream_id; - } uni; - } local; - - struct { - /* transport_params is the received transport parameters during - handshake. It is used for Short packet only. */ - ngtcp2_transport_params transport_params; - /* pending_transport_params is received transport parameters - during handshake. It is copied to transport_params when 1RTT - key is available. */ - ngtcp2_transport_params pending_transport_params; - struct { - ngtcp2_idtr idtr; - /* unsent_max_streams is the maximum number of streams of peer - initiated bidirectional stream which the local endpoint can - accept. This limit is not yet notified to the remote - endpoint. */ - uint64_t unsent_max_streams; - /* max_streams is the maximum number of streams of peer - initiated bidirectional stream which the local endpoint can - accept. */ - uint64_t max_streams; - } bidi; - - struct { - ngtcp2_idtr idtr; - /* unsent_max_streams is the maximum number of streams of peer - initiated unidirectional stream which the local endpoint can - accept. This limit is not yet notified to the remote - endpoint. */ - uint64_t unsent_max_streams; - /* max_streams is the maximum number of streams of peer - initiated unidirectional stream which the local endpoint can - accept. */ - uint64_t max_streams; - } uni; - } remote; - - struct { - struct { - /* new_tx_ckm is a new sender 1RTT key which has not been - used. */ - ngtcp2_crypto_km *new_tx_ckm; - /* new_rx_ckm is a new receiver 1RTT key which has not - successfully decrypted incoming packet yet. */ - ngtcp2_crypto_km *new_rx_ckm; - /* old_rx_ckm is an old receiver 1RTT key. */ - ngtcp2_crypto_km *old_rx_ckm; - /* confirmed_ts is the time instant when the key update is - confirmed by the local endpoint last time. UINT64_MAX means - undefined value. */ - ngtcp2_tstamp confirmed_ts; - } key_update; - - /* tls_native_handle is a native handle to TLS session object. */ - void *tls_native_handle; - size_t aead_overhead; - /* decrypt_buf is a buffer which is used to write decrypted data. */ - ngtcp2_vec decrypt_buf; - /* retry_aead is AEAD to verify Retry packet integrity. It is - used by client only. */ - ngtcp2_crypto_aead retry_aead; - /* retry_aead_ctx is AEAD cipher context to verify Retry packet - integrity. It is used by client only. */ - ngtcp2_crypto_aead_ctx retry_aead_ctx; - /* tls_error is TLS related error. */ - int tls_error; - } crypto; - - /* pkt contains the packet intermediate construction data to support - NGTCP2_WRITE_STREAM_FLAG_MORE */ - struct { - ngtcp2_crypto_cc cc; - ngtcp2_pkt_hd hd; - ngtcp2_ppe ppe; - ngtcp2_frame_chain **pfrc; - int pkt_empty; - int hd_logged; - uint8_t rtb_entry_flags; - int was_client_initial; - ngtcp2_ssize hs_spktlen; - } pkt; - - ngtcp2_map strms; - ngtcp2_conn_stat cstat; - ngtcp2_pv *pv; - ngtcp2_log log; - ngtcp2_qlog qlog; - ngtcp2_rst rst; - ngtcp2_cc_algo cc_algo; - ngtcp2_cc cc; - const ngtcp2_mem *mem; - /* idle_ts is the time instant when idle timer started. */ - ngtcp2_tstamp idle_ts; - void *user_data; - uint32_t version; - /* flags is bitwise OR of zero or more of ngtcp2_conn_flag. */ - uint16_t flags; - int server; -}; - -typedef enum ngtcp2_vmsg_type { - NGTCP2_VMSG_TYPE_STREAM, -} ngtcp2_vmsg_type; - -typedef struct ngtcp2_vmsg_stream { - /* strm is a stream that data is sent to. */ - ngtcp2_strm *strm; - /* flags is bitwise OR of zero or more of - ngtcp2_write_stream_flag. */ - uint32_t flags; - /* data is the pointer to ngtcp2_vec array which contains the stream - data to send. */ - const ngtcp2_vec *data; - /* datacnt is the number of ngtcp2_vec pointed by data. */ - size_t datacnt; - /* *pdatalen is the pointer to the variable which the number of - bytes written is assigned to if pdatalen is not NULL. */ - ngtcp2_ssize *pdatalen; -} ngtcp2_vmsg_stream; - -typedef struct ngtcp2_vmsg { - ngtcp2_vmsg_type type; - union { - ngtcp2_vmsg_stream stream; - }; -} ngtcp2_vmsg; - -/* - * ngtcp2_conn_sched_ack stores packet number |pkt_num| and its - * reception timestamp |ts| in order to send its ACK. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_PROTO - * Same packet number has already been added. - */ -int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr, - int64_t pkt_num, int active_ack, ngtcp2_tstamp ts); - -/* - * ngtcp2_conn_find_stream returns a stream whose stream ID is - * |stream_id|. If no such stream is found, it returns NULL. - */ -ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id); - -/* - * conn_init_stream initializes |strm|. Its stream ID is |stream_id|. - * This function adds |strm| to conn->strms. |strm| must be allocated - * by the caller. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_CALLBACK_FAILURE - * User-callback function failed. - */ -int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - int64_t stream_id, void *stream_user_data); - -/* - * ngtcp2_conn_close_stream closes stream |strm|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Stream is not found. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code); - -/* - * ngtcp2_conn_close_stream closes stream |strm| if no further - * transmission and reception are allowed, and all reordered incoming - * data are emitted to the application, and the transmitted data are - * acked. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Stream is not found. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code); - -/* - * ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest - * RTT which is not adjusted by ack delay. |ack_delay| is unscaled - * ack_delay included in ACK frame. |ack_delay| is actually tainted - * (sent by peer), so don't assume that |ack_delay| is always smaller - * than, or equals to |rtt|. - */ -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay); - -void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); - -/* - * ngtcp2_conn_detect_lost_pkt detects lost packets. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, - ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); - -/* - * ngtcp2_conn_tx_strmq_top returns the ngtcp2_strm which sits on the - * top of queue. tx_strmq must not be empty. - */ -ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn); - -/* - * ngtcp2_conn_tx_strmq_pop pops the ngtcp2_strm from the queue. - * tx_strmq must not be empty. - */ -void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn); - -/* - * ngtcp2_conn_tx_strmq_push pushes |strm| into tx_strmq. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm); - -/* - * ngtcp2_conn_internal_expiry returns the minimum expiry time among - * all timers in |conn|. - */ -ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); - -ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, - uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts); - -/* - * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr| - * frame only in the buffer pointed by |dest| whose length if - * |destlen|. |type| is a long packet type to send. If |type| is 0, - * Short packet is used. |dcid| is used as a destination connection - * ID. - * - * The packet written by this function will not be retransmitted. - * - * This function returns the number of bytes written in |dest| if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -ngtcp2_ssize -ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, uint8_t type, - const ngtcp2_cid *dcid, ngtcp2_frame *fr, - uint8_t rtb_flags, ngtcp2_tstamp ts); - -/* - * ngtcp2_conn_commit_local_transport_params commits the local - * transport parameters, which is currently set to - * conn->local.settings.transport_params. This function will do some - * amends on transport parameters for adjusting default values. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_INVALID_ARGUMENT - * CID in preferred address equals to the original SCID. - */ -int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn); - -/* - * ngtcp2_conn_lost_pkt_expiry returns the earliest expiry time of - * lost packet. - */ -ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn); - -/* - * ngtcp2_conn_remove_lost_pkt removes the expired lost packet. - */ -void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts); - -/* - * ngtcp2_conn_resched_frames reschedules frames linked from |*pfrc| - * for retransmission. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, - ngtcp2_frame_chain **pfrc); - -uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn); - -#endif /* NGTCP2_CONN_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/lib/ngtcp2_conv.c deleted file mode 100644 index 18cf3147913..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_conv.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_conv.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_str.h" -#include "ngtcp2_pkt.h" - -uint64_t ngtcp2_get_uint64(const uint8_t *p) { - uint64_t n; - memcpy(&n, p, 8); - return ngtcp2_ntohl64(n); -} - -uint64_t ngtcp2_get_uint48(const uint8_t *p) { - uint64_t n = 0; - memcpy(((uint8_t *)&n) + 2, p, 6); - return ngtcp2_ntohl64(n); -} - -uint32_t ngtcp2_get_uint32(const uint8_t *p) { - uint32_t n; - memcpy(&n, p, 4); - return ngtcp2_ntohl(n); -} - -uint32_t ngtcp2_get_uint24(const uint8_t *p) { - uint32_t n = 0; - memcpy(((uint8_t *)&n) + 1, p, 3); - return ngtcp2_ntohl(n); -} - -uint16_t ngtcp2_get_uint16(const uint8_t *p) { - uint16_t n; - memcpy(&n, p, 2); - return ngtcp2_ntohs(n); -} - -uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p) { - union { - char b[8]; - uint16_t n16; - uint32_t n32; - uint64_t n64; - } n; - - *plen = 1u << (*p >> 6); - - switch (*plen) { - case 1: - return *p; - case 2: - memcpy(&n, p, 2); - n.b[0] &= 0x3f; - return ngtcp2_ntohs(n.n16); - case 4: - memcpy(&n, p, 4); - n.b[0] &= 0x3f; - return ngtcp2_ntohl(n.n32); - case 8: - memcpy(&n, p, 8); - n.b[0] &= 0x3f; - return ngtcp2_ntohl64(n.n64); - } - - assert(0); -} - -int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen) { - switch (pkt_numlen) { - case 1: - return *p; - case 2: - return (int64_t)ngtcp2_get_uint16(p); - case 3: - return (int64_t)ngtcp2_get_uint24(p); - case 4: - return (int64_t)ngtcp2_get_uint32(p); - default: - assert(0); - } -} - -uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n) { - n = ngtcp2_htonl64(n); - return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); -} - -uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n) { - n = ngtcp2_htonl64(n); - return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 2, 6); -} - -uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n) { - n = ngtcp2_htonl(n); - return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); -} - -uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n) { - n = ngtcp2_htonl(n); - return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 1, 3); -} - -uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n) { - n = ngtcp2_htons(n); - return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); -} - -uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) { - uint8_t *rv; - if (n < 64) { - *p++ = (uint8_t)n; - return p; - } - if (n < 16384) { - rv = ngtcp2_put_uint16be(p, (uint16_t)n); - *p |= 0x40; - return rv; - } - if (n < 1073741824) { - rv = ngtcp2_put_uint32be(p, (uint32_t)n); - *p |= 0x80; - return rv; - } - assert(n < 4611686018427387904ULL); - rv = ngtcp2_put_uint64be(p, n); - *p |= 0xc0; - return rv; -} - -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) { - uint8_t *rv; - - assert(n < 16384); - - rv = ngtcp2_put_uint16be(p, n); - *p |= 0x40; - - return rv; -} - -uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len) { - switch (len) { - case 1: - *p++ = (uint8_t)pkt_num; - return p; - case 2: - ngtcp2_put_uint16be(p, (uint16_t)pkt_num); - return p + 2; - case 3: - ngtcp2_put_uint24be(p, (uint32_t)pkt_num); - return p + 3; - case 4: - ngtcp2_put_uint32be(p, (uint32_t)pkt_num); - return p + 4; - default: - assert(0); - } -} - -size_t ngtcp2_get_varint_len(const uint8_t *p) { return 1u << (*p >> 6); } - -size_t ngtcp2_put_varint_len(uint64_t n) { - if (n < 64) { - return 1; - } - if (n < 16384) { - return 2; - } - if (n < 1073741824) { - return 4; - } - assert(n < 4611686018427387904ULL); - return 8; -} - -int64_t ngtcp2_nth_server_bidi_id(uint64_t n) { - if (n == 0) { - return 0; - } - - if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { - return NGTCP2_MAX_SERVER_STREAM_ID_BIDI; - } - - return (int64_t)(((n - 1) << 2) | 0x01); -} - -int64_t ngtcp2_nth_client_bidi_id(uint64_t n) { - if (n == 0) { - return 0; - } - - if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { - return NGTCP2_MAX_CLIENT_STREAM_ID_BIDI; - } - - return (int64_t)((n - 1) << 2); -} - -int64_t ngtcp2_nth_server_uni_id(uint64_t n) { - if (n == 0) { - return 0; - } - - if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { - return NGTCP2_MAX_SERVER_STREAM_ID_UNI; - } - - return (int64_t)(((n - 1) << 2) | 0x03); -} - -int64_t ngtcp2_nth_client_uni_id(uint64_t n) { - if (n == 0) { - return 0; - } - - if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { - return NGTCP2_MAX_CLIENT_STREAM_ID_UNI; - } - - return (int64_t)(((n - 1) << 2) | 0x02); -} - -uint64_t ngtcp2_ord_stream_id(int64_t stream_id) { - return (uint64_t)(stream_id >> 2) + 1; -} diff --git a/deps/ngtcp2/lib/ngtcp2_conv.h b/deps/ngtcp2/lib/ngtcp2_conv.h deleted file mode 100644 index 227470b9192..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_conv.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_CONV_H -#define NGTCP2_CONV_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#ifdef HAVE_ARPA_INET_H -# include <arpa/inet.h> -#endif /* HAVE_ARPA_INET_H */ - -#ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -#endif /* HAVE_NETINET_IN_H */ - -#ifdef HAVE_BYTESWAP_H -# include <byteswap.h> -#endif /* HAVE_BYTESWAP_H */ - -#ifdef HAVE_ENDIAN_H -# include <endian.h> -#endif /* HAVE_ENDIAN_H */ - -#ifdef HAVE_SYS_ENDIAN_H -# include <sys/endian.h> -#endif /* HAVE_SYS_ENDIAN_H */ - -#include <ngtcp2/ngtcp2.h> - -#if defined HAVE_BSWAP_64 || HAVE_DECL_BSWAP_64 -# define ngtcp2_bswap64 bswap_64 -#else /* !HAVE_BSWAP_64 */ -# define ngtcp2_bswap64(N) \ - ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) -#endif /* !HAVE_BSWAP_64 */ - -#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH -# define ngtcp2_ntohl64(N) be64toh(N) -# define ngtcp2_htonl64(N) htobe64(N) -#else /* !HAVE_BE64TOH */ -# if defined WORDS_BIGENDIAN -# define ngtcp2_ntohl64(N) (N) -# define ngtcp2_htonl64(N) (N) -# else /* !WORDS_BIGENDIAN */ -# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) -# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) -# endif /* !WORDS_BIGENDIAN */ -#endif /* !HAVE_BE64TOH */ - -#if defined(WIN32) -/* Windows requires ws2_32 library for ntonl family functions. We - define inline functions for those function so that we don't have - dependeny on that lib. */ - -# ifdef _MSC_VER -# define STIN static __inline -# else -# define STIN static inline -# endif - -STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; - *p++ = (hostlong >> 16) & 0xffu; - *p++ = (hostlong >> 8) & 0xffu; - *p = hostlong & 0xffu; - return res; -} - -STIN uint16_t ngtcp2_htons(uint16_t hostshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; - *p = hostshort & 0xffu; - return res; -} - -STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; - res += *p; - return res; -} - -STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; - res += *p; - return res; -} - -#else /* !WIN32 */ - -# define ngtcp2_htonl htonl -# define ngtcp2_htons htons -# define ngtcp2_ntohl ntohl -# define ngtcp2_ntohs ntohs - -#endif /* !WIN32 */ - -/* - * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned - * integer encoded as network byte order, and returns it in host byte - * order. - */ -uint64_t ngtcp2_get_uint64(const uint8_t *p); - -/* - * ngtcp2_get_uint48 reads 6 bytes from |p| as 48 bits unsigned - * integer encoded as network byte order, and returns it in host byte - * order. - */ -uint64_t ngtcp2_get_uint48(const uint8_t *p); - -/* - * ngtcp2_get_uint32 reads 4 bytes from |p| as 32 bits unsigned - * integer encoded as network byte order, and returns it in host byte - * order. - */ -uint32_t ngtcp2_get_uint32(const uint8_t *p); - -/* - * ngtcp2_get_uint24 reads 3 bytes from |p| as 24 bits unsigned - * integer encoded as network byte order, and returns it in host byte - * order. - */ -uint32_t ngtcp2_get_uint24(const uint8_t *p); - -/* - * ngtcp2_get_uint16 reads 2 bytes from |p| as 16 bits unsigned - * integer encoded as network byte order, and returns it in host byte - * order. - */ -uint16_t ngtcp2_get_uint16(const uint8_t *p); - -/* - * ngtcp2_get_varint reads variable-length integer from |p|, and - * returns it in host byte order. The number of bytes read is stored - * in |*plen|. - */ -uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p); - -/* - * ngtcp2_get_pkt_num reads encoded packet number from |p|. The - * packet number is encoed in |pkt_numlen| bytes. - */ -int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen); - -/* - * ngtcp2_put_uint64be writes |n| in host byte order in |p| in network - * byte order. It returns the one beyond of the last written - * position. - */ -uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n); - -/* - * ngtcp2_put_uint48be writes |n| in host byte order in |p| in network - * byte order. It writes only least significant 48 bits. It returns - * the one beyond of the last written position. - */ -uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n); - -/* - * ngtcp2_put_uint32be writes |n| in host byte order in |p| in network - * byte order. It returns the one beyond of the last written - * position. - */ -uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n); - -/* - * ngtcp2_put_uint24be writes |n| in host byte order in |p| in network - * byte order. It writes only least significant 24 bits. It returns - * the one beyond of the last written position. - */ -uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n); - -/* - * ngtcp2_put_uint16be writes |n| in host byte order in |p| in network - * byte order. It returns the one beyond of the last written - * position. - */ -uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n); - -/* - * ngtcp2_put_varint writes |n| in |p| using variable-length integer - * encoding. It returns the one beyond of the last written position. - */ -uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n); - -/* - * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer - * encoding. |n| must be strictly less than 16384. The function - * always encodes |n| in 2 bytes. It returns the one beyond of the - * last written position. - */ -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n); - -/* - * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It - * returns the one beyond of the last written position. - */ -uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len); - -/* - * ngtcp2_get_varint_len returns the required number of bytes to read - * variable-length integer starting at |p|. - */ -size_t ngtcp2_get_varint_len(const uint8_t *p); - -/* - * ngtcp2_put_varint_len returns the required number of bytes to - * encode |n|. - */ -size_t ngtcp2_put_varint_len(uint64_t n); - -/* - * ngtcp2_nth_server_bidi_id returns |n|-th server bidirectional - * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is - * larger than NGTCP2_MAX_SERVER_STREAM_ID_BIDI, this function returns - * NGTCP2_MAX_SERVER_STREAM_ID_BIDI. - */ -int64_t ngtcp2_nth_server_bidi_id(uint64_t n); - -/* - * ngtcp2_nth_client_bidi_id returns |n|-th client bidirectional - * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is - * larger than NGTCP2_MAX_CLIENT_STREAM_ID_BIDI, this function returns - * NGTCP2_MAX_CLIENT_STREAM_ID_BIDI. - */ -int64_t ngtcp2_nth_client_bidi_id(uint64_t n); - -/* - * ngtcp2_nth_server_uni_id returns |n|-th server unidirectional - * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is - * larger than NGTCP2_MAX_SERVER_STREAM_ID_UNI, this function returns - * NGTCP2_MAX_SERVER_STREAM_ID_UNI. - */ -int64_t ngtcp2_nth_server_uni_id(uint64_t n); - -/* - * ngtcp2_nth_client_uni_id returns |n|-th client unidirectional - * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is - * larger than NGTCP2_MAX_CLIENT_STREAM_ID_UNI, this function returns - * NGTCP2_MAX_CLIENT_STREAM_ID_UNI. - */ -int64_t ngtcp2_nth_client_uni_id(uint64_t n); - -/* - * ngtcp2_ord_stream_id returns the ordinal number of |stream_id|. - */ -uint64_t ngtcp2_ord_stream_id(int64_t stream_id); - -#endif /* NGTCP2_CONV_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/lib/ngtcp2_crypto.c deleted file mode 100644 index 5cfe145ec9b..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_crypto.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_crypto.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_str.h" -#include "ngtcp2_conv.h" -#include "ngtcp2_conn.h" - -int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, - size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, - const ngtcp2_mem *mem) { - int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, ivlen, mem); - if (rv != 0) { - return rv; - } - - if (secretlen) { - memcpy((*pckm)->secret.base, secret, secretlen); - } - if (aead_ctx) { - (*pckm)->aead_ctx = *aead_ctx; - } - memcpy((*pckm)->iv.base, iv, ivlen); - - return 0; -} - -int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, - size_t ivlen, const ngtcp2_mem *mem) { - size_t len; - uint8_t *p; - - len = sizeof(ngtcp2_crypto_km) + secretlen + ivlen; - - *pckm = ngtcp2_mem_malloc(mem, len); - if (*pckm == NULL) { - return NGTCP2_ERR_NOMEM; - } - - p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km); - (*pckm)->secret.base = p; - (*pckm)->secret.len = secretlen; - p += secretlen; - (*pckm)->iv.base = p; - (*pckm)->iv.len = ivlen; - (*pckm)->aead_ctx.native_handle = NULL; - (*pckm)->pkt_num = -1; - (*pckm)->use_count = 0; - (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE; - - return 0; -} - -void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem) { - if (ckm == NULL) { - return; - } - - ngtcp2_mem_free(mem, ckm); -} - -void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, - int64_t pkt_num) { - size_t i; - uint64_t n; - - memcpy(dest, iv, ivlen); - n = ngtcp2_htonl64((uint64_t)pkt_num); - - for (i = 0; i < 8; ++i) { - dest[ivlen - 8 + i] ^= ((uint8_t *)&n)[i]; - } -} - -/* - * varint_paramlen returns the length of a single transport parameter - * which has variable integer in its parameter. - */ -static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t param) { - size_t valuelen = ngtcp2_put_varint_len(param); - return ngtcp2_put_varint_len(id) + ngtcp2_put_varint_len(valuelen) + valuelen; -} - -/* - * write_varint_param writes parameter |id| of the given |value| in - * varint encoding. It returns p + the number of bytes written. - */ -static uint8_t *write_varint_param(uint8_t *p, ngtcp2_transport_param_id id, - uint64_t value) { - p = ngtcp2_put_varint(p, id); - p = ngtcp2_put_varint(p, ngtcp2_put_varint_len(value)); - return ngtcp2_put_varint(p, value); -} - -/* - * cid_paramlen returns the length of a single transport parameter - * which has |cid| as value. - */ -static size_t cid_paramlen(ngtcp2_transport_param_id id, - const ngtcp2_cid *cid) { - return ngtcp2_put_varint_len(id) + ngtcp2_put_varint_len(cid->datalen) + - cid->datalen; -} - -/* - * write_cid_param writes parameter |id| of the given |cid|. It - * returns p + the number of bytes written. - */ -static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id, - const ngtcp2_cid *cid) { - assert(cid->datalen == 0 || cid->datalen >= NGTCP2_MIN_CIDLEN); - assert(cid->datalen <= NGTCP2_MAX_CIDLEN); - - p = ngtcp2_put_varint(p, id); - p = ngtcp2_put_varint(p, cid->datalen); - if (cid->datalen) { - p = ngtcp2_cpymem(p, cid->data, cid->datalen); - } - return p; -} - -ngtcp2_ssize -ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, - ngtcp2_transport_params_type exttype, - const ngtcp2_transport_params *params) { - uint8_t *p; - size_t len = 0; - /* For some reason, gcc 7.3.0 requires this initialization. */ - size_t preferred_addrlen = 0; - - switch (exttype) { - case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO: - break; - case NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS: - len += - cid_paramlen(NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, - ¶ms->original_dcid); - - if (params->stateless_reset_token_present) { - len += - ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) + - ngtcp2_put_varint_len(NGTCP2_STATELESS_RESET_TOKENLEN) + - NGTCP2_STATELESS_RESET_TOKENLEN; - } - if (params->preferred_address_present) { - assert(params->preferred_address.cid.datalen >= NGTCP2_MIN_CIDLEN); - assert(params->preferred_address.cid.datalen <= NGTCP2_MAX_CIDLEN); - preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ + - 16 /* ipv6Address */ + 2 /* ipv6Port */ - + 1 + - params->preferred_address.cid.datalen /* CID */ + - NGTCP2_STATELESS_RESET_TOKENLEN; - len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) + - ngtcp2_put_varint_len(preferred_addrlen) + preferred_addrlen; - } - if (params->retry_scid_present) { - len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, - ¶ms->retry_scid); - } - break; - default: - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, - ¶ms->initial_scid); - - if (params->initial_max_stream_data_bidi_local) { - len += varint_paramlen( - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, - params->initial_max_stream_data_bidi_local); - } - if (params->initial_max_stream_data_bidi_remote) { - len += varint_paramlen( - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, - params->initial_max_stream_data_bidi_remote); - } - if (params->initial_max_stream_data_uni) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, - params->initial_max_stream_data_uni); - } - if (params->initial_max_data) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, - params->initial_max_data); - } - if (params->initial_max_streams_bidi) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, - params->initial_max_streams_bidi); - } - if (params->initial_max_streams_uni) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, - params->initial_max_streams_uni); - } - if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, - params->max_udp_payload_size); - } - if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, - params->ack_delay_exponent); - } - if (params->disable_active_migration) { - len += - ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) + - ngtcp2_put_varint_len(0); - } - if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, - params->max_ack_delay / NGTCP2_MILLISECONDS); - } - if (params->max_idle_timeout) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, - params->max_idle_timeout / NGTCP2_MILLISECONDS); - } - if (params->active_connection_id_limit && - params->active_connection_id_limit != - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { - len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, - params->active_connection_id_limit); - } - - if (destlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = dest; - - if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { - p = write_cid_param( - p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, - ¶ms->original_dcid); - - if (params->stateless_reset_token_present) { - p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN); - p = ngtcp2_put_varint(p, sizeof(params->stateless_reset_token)); - p = ngtcp2_cpymem(p, params->stateless_reset_token, - sizeof(params->stateless_reset_token)); - } - if (params->preferred_address_present) { - p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS); - p = ngtcp2_put_varint(p, preferred_addrlen); - - p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr, - sizeof(params->preferred_address.ipv4_addr)); - p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port); - - p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr, - sizeof(params->preferred_address.ipv6_addr)); - p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port); - - *p++ = (uint8_t)params->preferred_address.cid.datalen; - if (params->preferred_address.cid.datalen) { - p = ngtcp2_cpymem(p, params->preferred_address.cid.data, - params->preferred_address.cid.datalen); - } - p = ngtcp2_cpymem( - p, params->preferred_address.stateless_reset_token, - sizeof(params->preferred_address.stateless_reset_token)); - } - if (params->retry_scid_present) { - p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, - ¶ms->retry_scid); - } - } - - p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, - ¶ms->initial_scid); - - if (params->initial_max_stream_data_bidi_local) { - p = write_varint_param( - p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, - params->initial_max_stream_data_bidi_local); - } - - if (params->initial_max_stream_data_bidi_remote) { - p = write_varint_param( - p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, - params->initial_max_stream_data_bidi_remote); - } - - if (params->initial_max_stream_data_uni) { - p = write_varint_param(p, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, - params->initial_max_stream_data_uni); - } - - if (params->initial_max_data) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, - params->initial_max_data); - } - - if (params->initial_max_streams_bidi) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, - params->initial_max_streams_bidi); - } - - if (params->initial_max_streams_uni) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, - params->initial_max_streams_uni); - } - - if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, - params->max_udp_payload_size); - } - - if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, - params->ack_delay_exponent); - } - - if (params->disable_active_migration) { - p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION); - p = ngtcp2_put_varint(p, 0); - } - - if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, - params->max_ack_delay / NGTCP2_MILLISECONDS); - } - - if (params->max_idle_timeout) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, - params->max_idle_timeout / NGTCP2_MILLISECONDS); - } - - if (params->active_connection_id_limit && - params->active_connection_id_limit != - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, - params->active_connection_id_limit); - } - - assert((size_t)(p - dest) == len); - - return (ngtcp2_ssize)len; -} - -/* - * decode_varint decodes a single varint from the buffer pointed by - * |p| of length |end - p|. If it decodes an integer successfully, it - * stores the integer in |*pdest| and returns 0. Otherwise it returns - * -1. - */ -static ngtcp2_ssize decode_varint(uint64_t *pdest, const uint8_t *p, - const uint8_t *end) { - size_t len; - - if (p == end) { - return -1; - } - - len = ngtcp2_get_varint_len(p); - if ((uint64_t)(end - p) < len) { - return -1; - } - - *pdest = ngtcp2_get_varint(&len, p); - - return (ngtcp2_ssize)len; -} - -/* - * decode_varint_param decodes length prefixed value from the buffer - * pointed by |p| of length |end - p|. The length and value are - * encoded in varint form. If it decodes a value successfully, it - * stores the value in |*pdest| and returns 0. Otherwise it returns - * -1. - */ -static ngtcp2_ssize decode_varint_param(uint64_t *pdest, const uint8_t *p, - const uint8_t *end) { - const uint8_t *begin = p; - ngtcp2_ssize nread; - uint64_t valuelen; - size_t n; - - nread = decode_varint(&valuelen, p, end); - if (nread < 0) { - return -1; - } - - p += nread; - - if (p == end) { - return -1; - } - - if ((uint64_t)(end - p) < valuelen) { - return -1; - } - - if (ngtcp2_get_varint_len(p) != valuelen) { - return -1; - } - - *pdest = ngtcp2_get_varint(&n, p); - - p += valuelen; - - return (ngtcp2_ssize)(p - begin); -} - -/* - * decode_cid_param decodes length prefixed ngtcp2_cid from the buffer - * pointed by |p| of length |end - p|. The length is encoded in - * varint form. If it decodes a value successfully, it stores the - * value in |*pdest| and returns the number of bytes read. Otherwise - * it returns -1. - */ -static ngtcp2_ssize decode_cid_param(ngtcp2_cid *pdest, const uint8_t *p, - const uint8_t *end) { - const uint8_t *begin = p; - uint64_t valuelen; - ngtcp2_ssize nread = decode_varint(&valuelen, p, end); - - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - - p += nread; - - if ((valuelen != 0 && valuelen < NGTCP2_MIN_CIDLEN) || - valuelen > NGTCP2_MAX_CIDLEN || (size_t)(end - p) < valuelen) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - - ngtcp2_cid_init(pdest, p, (size_t)valuelen); - - p += valuelen; - - return (ngtcp2_ssize)(p - begin); -} - -int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, - ngtcp2_transport_params_type exttype, - const uint8_t *data, size_t datalen) { - const uint8_t *p, *end; - size_t len; - uint64_t param_type; - uint64_t valuelen; - ngtcp2_ssize nread; - int initial_scid_present = 0; - int original_dcid_present = 0; - - p = data; - end = data + datalen; - - /* Set default values */ - memset(params, 0, sizeof(*params)); - params->initial_max_streams_bidi = 0; - params->initial_max_streams_uni = 0; - params->initial_max_stream_data_bidi_local = 0; - params->initial_max_stream_data_bidi_remote = 0; - params->initial_max_stream_data_uni = 0; - params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; - params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; - params->stateless_reset_token_present = 0; - params->preferred_address_present = 0; - params->disable_active_migration = 0; - params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; - params->max_idle_timeout = 0; - params->active_connection_id_limit = - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; - params->retry_scid_present = 0; - memset(¶ms->retry_scid, 0, sizeof(params->retry_scid)); - memset(¶ms->initial_scid, 0, sizeof(params->initial_scid)); - memset(¶ms->original_dcid, 0, sizeof(params->original_dcid)); - - if (datalen == 0) { - return 0; - } - - for (; (size_t)(end - p) >= 2;) { - nread = decode_varint(¶m_type, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - - switch (param_type) { - case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: - nread = decode_varint_param(¶ms->initial_max_stream_data_bidi_local, - p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: - nread = decode_varint_param(¶ms->initial_max_stream_data_bidi_remote, - p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI: - nread = decode_varint_param(¶ms->initial_max_stream_data_uni, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA: - nread = decode_varint_param(¶ms->initial_max_data, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI: - nread = decode_varint_param(¶ms->initial_max_streams_bidi, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - if (params->initial_max_streams_bidi > NGTCP2_MAX_STREAMS) { - return NGTCP2_ERR_STREAM_LIMIT; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI: - nread = decode_varint_param(¶ms->initial_max_streams_uni, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - if (params->initial_max_streams_uni > NGTCP2_MAX_STREAMS) { - return NGTCP2_ERR_STREAM_LIMIT; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT: - nread = decode_varint_param(¶ms->max_idle_timeout, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - params->max_idle_timeout *= NGTCP2_MILLISECONDS; - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE: - nread = decode_varint_param(¶ms->max_udp_payload_size, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN: - if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - nread = decode_varint(&valuelen, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - if ((size_t)valuelen != sizeof(params->stateless_reset_token)) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - if ((size_t)(end - p) < sizeof(params->stateless_reset_token)) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - - memcpy(params->stateless_reset_token, p, - sizeof(params->stateless_reset_token)); - params->stateless_reset_token_present = 1; - - p += sizeof(params->stateless_reset_token); - break; - case NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT: - nread = decode_varint_param(¶ms->ack_delay_exponent, p, end); - if (nread < 0 || params->ack_delay_exponent > 20) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS: - if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - nread = decode_varint(&valuelen, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - if ((size_t)(end - p) < valuelen) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - len = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ + - 2 /* ipv6Port */ - + 1 /* cid length */ + NGTCP2_STATELESS_RESET_TOKENLEN; - if (valuelen < len) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - - memcpy(params->preferred_address.ipv4_addr, p, - sizeof(params->preferred_address.ipv4_addr)); - p += sizeof(params->preferred_address.ipv4_addr); - params->preferred_address.ipv4_port = ngtcp2_get_uint16(p); - p += sizeof(uint16_t); - - memcpy(params->preferred_address.ipv6_addr, p, - sizeof(params->preferred_address.ipv6_addr)); - p += sizeof(params->preferred_address.ipv6_addr); - params->preferred_address.ipv6_port = ngtcp2_get_uint16(p); - p += sizeof(uint16_t); - - /* cid */ - params->preferred_address.cid.datalen = *p++; - len += params->preferred_address.cid.datalen; - if (valuelen != len || - params->preferred_address.cid.datalen > NGTCP2_MAX_CIDLEN || - params->preferred_address.cid.datalen < NGTCP2_MIN_CIDLEN) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - if (params->preferred_address.cid.datalen) { - memcpy(params->preferred_address.cid.data, p, - params->preferred_address.cid.datalen); - p += params->preferred_address.cid.datalen; - } - - /* stateless reset token */ - memcpy(params->preferred_address.stateless_reset_token, p, - sizeof(params->preferred_address.stateless_reset_token)); - p += sizeof(params->preferred_address.stateless_reset_token); - params->preferred_address_present = 1; - break; - case NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION: - nread = decode_varint(&valuelen, p, end); - if (nread < 0 || valuelen != 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - params->disable_active_migration = 1; - break; - case NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID: - if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - nread = decode_cid_param(¶ms->original_dcid, p, end); - if (nread < 0) { - return (int)nread; - } - original_dcid_present = 1; - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID: - if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - nread = decode_cid_param(¶ms->retry_scid, p, end); - if (nread < 0) { - return (int)nread; - } - params->retry_scid_present = 1; - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID: - nread = decode_cid_param(¶ms->initial_scid, p, end); - if (nread < 0) { - return (int)nread; - } - initial_scid_present = 1; - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY: - nread = decode_varint_param(¶ms->max_ack_delay, p, end); - if (nread < 0 || params->max_ack_delay >= 16384) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - params->max_ack_delay *= NGTCP2_MILLISECONDS; - p += nread; - break; - case NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT: - nread = decode_varint_param(¶ms->active_connection_id_limit, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - break; - default: - /* Ignore unknown parameter */ - nread = decode_varint(&valuelen, p, end); - if (nread < 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += nread; - if ((size_t)(end - p) < valuelen) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - p += valuelen; - break; - } - } - - if (end - p != 0) { - return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; - } - - if (!initial_scid_present || - (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS && - !original_dcid_present)) { - return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; - } - - return 0; -} diff --git a/deps/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/lib/ngtcp2_crypto.h deleted file mode 100644 index b1baf30a85f..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_crypto.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_H -#define NGTCP2_CRYPTO_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" - -/* NGTCP2_INITIAL_AEAD_OVERHEAD is an overhead of AEAD used by Initial - packets. Because QUIC uses AEAD_AES_128_GCM, the overhead is 16 - bytes. */ -#define NGTCP2_INITIAL_AEAD_OVERHEAD 16 - -/* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */ -#define NGTCP2_MAX_AEAD_OVERHEAD 16 - -typedef enum { - NGTCP2_CRYPTO_KM_FLAG_NONE, - /* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is - set. */ - NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE = 0x01, -} ngtcp2_crypto_km_flag; - -typedef struct { - ngtcp2_vec secret; - ngtcp2_crypto_aead_ctx aead_ctx; - ngtcp2_vec iv; - /* pkt_num is a packet number of a packet which uses this keying - material. For encryption key, it is the lowest packet number of - a packet. For decryption key, it is the lowest packet number of - a packet which can be decrypted with this keying material. */ - int64_t pkt_num; - /* use_count is the number of encryption or decryption failure. For - tx key, this is the number of encryption. For rx key, this is - the number of decryption failure. */ - uint64_t use_count; - /* flags is the bitwise OR of zero or more of - ngtcp2_crypto_km_flag. */ - uint8_t flags; -} ngtcp2_crypto_km; - -/* - * ngtcp2_crypto_km_new creates new ngtcp2_crypto_km object and - * assigns its pointer to |*pckm|. The |secret| of length - * |secretlen|, the |key| of length |keylen| and the |iv| of length - * |ivlen| are copied to |*pckm|. If |secretlen| == 0, the function - * assumes no secret is given which is acceptable. The sole reason to - * store secret is update keys. Only 1RTT key can be updated. - */ -int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, - size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, - const ngtcp2_mem *mem); - -/* - * ngtcp2_crypto_km_nocopy_new is similar to ngtcp2_crypto_km_new, but - * it does not copy secret, key and IV. - */ -int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, - size_t ivlen, const ngtcp2_mem *mem); - -void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem); - -typedef struct { - ngtcp2_crypto_aead aead; - ngtcp2_crypto_cipher hp; - ngtcp2_crypto_km *ckm; - ngtcp2_crypto_cipher_ctx hp_ctx; - size_t aead_overhead; - ngtcp2_encrypt encrypt; - ngtcp2_decrypt decrypt; - ngtcp2_hp_mask hp_mask; -} ngtcp2_crypto_cc; - -void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, - int64_t pkt_num); - -#endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/lib/ngtcp2_err.c deleted file mode 100644 index 5a2425367d4..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_err.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_err.h" - -const char *ngtcp2_strerror(int liberr) { - switch (liberr) { - case 0: - return "NO_ERROR"; - case NGTCP2_ERR_INVALID_ARGUMENT: - return "ERR_INVALID_ARGUMENT"; - case NGTCP2_ERR_UNKNOWN_PKT_TYPE: - return "ERR_UNKNOWN_PKT_TYPE"; - case NGTCP2_ERR_NOBUF: - return "ERR_NOBUF"; - case NGTCP2_ERR_PROTO: - return "ERR_PROTO"; - case NGTCP2_ERR_INVALID_STATE: - return "ERR_INVALID_STATE"; - case NGTCP2_ERR_ACK_FRAME: - return "ERR_ACK_FRAME"; - case NGTCP2_ERR_STREAM_ID_BLOCKED: - return "ERR_STREAM_ID_BLOCKED"; - case NGTCP2_ERR_STREAM_IN_USE: - return "ERR_STREAM_IN_USE"; - case NGTCP2_ERR_STREAM_DATA_BLOCKED: - return "ERR_STREAM_DATA_BLOCKED"; - case NGTCP2_ERR_FLOW_CONTROL: - return "ERR_FLOW_CONTROL"; - case NGTCP2_ERR_CONNECTION_ID_LIMIT: - return "ERR_CONNECTION_ID_LIMIT"; - case NGTCP2_ERR_STREAM_LIMIT: - return "ERR_STREAM_LIMIT"; - case NGTCP2_ERR_FINAL_SIZE: - return "ERR_FINAL_SIZE"; - case NGTCP2_ERR_CRYPTO: - return "ERR_CRYPTO"; - case NGTCP2_ERR_PKT_NUM_EXHAUSTED: - return "ERR_PKT_NUM_EXHAUSTED"; - case NGTCP2_ERR_NOMEM: - return "ERR_NOMEM"; - case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: - return "ERR_REQUIRED_TRANSPORT_PARAM"; - case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: - return "ERR_MALFORMED_TRANSPORT_PARAM"; - case NGTCP2_ERR_FRAME_ENCODING: - return "ERR_FRAME_ENCODING"; - case NGTCP2_ERR_TLS_DECRYPT: - return "ERR_TLS_DECRYPT"; - case NGTCP2_ERR_STREAM_SHUT_WR: - return "ERR_STREAM_SHUT_WR"; - case NGTCP2_ERR_STREAM_NOT_FOUND: - return "ERR_STREAM_NOT_FOUND"; - case NGTCP2_ERR_STREAM_STATE: - return "ERR_STREAM_STATE"; - case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: - return "ERR_RECV_VERSION_NEGOTIATION"; - case NGTCP2_ERR_CLOSING: - return "ERR_CLOSING"; - case NGTCP2_ERR_DRAINING: - return "ERR_DRAINING"; - case NGTCP2_ERR_TRANSPORT_PARAM: - return "ERR_TRANSPORT_PARAM"; - case NGTCP2_ERR_DISCARD_PKT: - return "ERR_DISCARD_PKT"; - case NGTCP2_ERR_PATH_VALIDATION_FAILED: - return "ERR_PATH_VALIDATION_FAILED"; - case NGTCP2_ERR_CONN_ID_BLOCKED: - return "ERR_CONN_ID_BLOCKED"; - case NGTCP2_ERR_CALLBACK_FAILURE: - return "ERR_CALLBACK_FAILURE"; - case NGTCP2_ERR_INTERNAL: - return "ERR_INTERNAL"; - case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: - return "ERR_CRYPTO_BUFFER_EXCEEDED"; - case NGTCP2_ERR_WRITE_MORE: - return "ERR_WRITE_MORE"; - case NGTCP2_ERR_RETRY: - return "ERR_RETRY"; - case NGTCP2_ERR_DROP_CONN: - return "ERR_DROP_CONN"; - default: - return "(unknown)"; - } -} - -int ngtcp2_err_is_fatal(int liberr) { return liberr < NGTCP2_ERR_FATAL; } - -uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { - switch (liberr) { - case 0: - return NGTCP2_NO_ERROR; - case NGTCP2_ERR_ACK_FRAME: - case NGTCP2_ERR_FRAME_ENCODING: - return NGTCP2_FRAME_ENCODING_ERROR; - case NGTCP2_ERR_FLOW_CONTROL: - return NGTCP2_FLOW_CONTROL_ERROR; - case NGTCP2_ERR_CONNECTION_ID_LIMIT: - return NGTCP2_CONNECTION_ID_LIMIT_ERROR; - case NGTCP2_ERR_STREAM_LIMIT: - return NGTCP2_STREAM_LIMIT_ERROR; - case NGTCP2_ERR_FINAL_SIZE: - return NGTCP2_FINAL_SIZE_ERROR; - case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: - case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: - case NGTCP2_ERR_TRANSPORT_PARAM: - return NGTCP2_TRANSPORT_PARAMETER_ERROR; - case NGTCP2_ERR_INVALID_ARGUMENT: - return NGTCP2_INTERNAL_ERROR; - case NGTCP2_ERR_STREAM_STATE: - return NGTCP2_STREAM_STATE_ERROR; - case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: - return NGTCP2_CRYPTO_BUFFER_EXCEEDED; - default: - return NGTCP2_PROTOCOL_VIOLATION; - } -} diff --git a/deps/ngtcp2/lib/ngtcp2_err.h b/deps/ngtcp2/lib/ngtcp2_err.h deleted file mode 100644 index 9229f5425a6..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_err.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_ERR_H -#define NGTCP2_ERR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#endif /* NGTCP2_ERR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/lib/ngtcp2_gaptr.c deleted file mode 100644 index 6e7f3b7e554..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_gaptr.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_gaptr.h" -#include "ngtcp2_range.h" - -#include <string.h> -#include <assert.h> - -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { - int rv; - ngtcp2_range range = {0, UINT64_MAX}; - - rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL); - if (rv != 0) { - ngtcp2_ksl_free(&gaptr->gap); - return rv; - } - - gaptr->mem = mem; - - return 0; -} - -void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) { - if (gaptr == NULL) { - return; - } - - ngtcp2_ksl_free(&gaptr->gap); -} - -int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { - int rv; - ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; - ngtcp2_ksl_it it; - - it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, - ngtcp2_ksl_range_exclusive_compar); - - for (; !ngtcp2_ksl_it_end(&it);) { - k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - m = ngtcp2_range_intersect(&q, &k); - if (!ngtcp2_range_len(&m)) { - break; - } - - if (ngtcp2_range_eq(&k, &m)) { - ngtcp2_ksl_remove(&gaptr->gap, &it, &k); - continue; - } - ngtcp2_range_cut(&l, &r, &k, &m); - if (ngtcp2_range_len(&l)) { - ngtcp2_ksl_update_key(&gaptr->gap, &k, &l); - - if (ngtcp2_range_len(&r)) { - rv = ngtcp2_ksl_insert(&gaptr->gap, &it, &r, NULL); - if (rv != 0) { - return rv; - } - } - } else if (ngtcp2_range_len(&r)) { - ngtcp2_ksl_update_key(&gaptr->gap, &k, &r); - } - ngtcp2_ksl_it_next(&it); - } - return 0; -} - -uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); - ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - return r.begin; -} - -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset) { - ngtcp2_range q = {offset, offset + 1}; - return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, - ngtcp2_ksl_range_exclusive_compar); -} - -int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, - size_t datalen) { - ngtcp2_range q = {offset, offset + datalen}; - ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar( - &gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); - ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - ngtcp2_range m = ngtcp2_range_intersect(&q, &k); - return ngtcp2_range_len(&m) == 0; -} - -void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); - ngtcp2_range r; - - assert(!ngtcp2_ksl_it_end(&it)); - - r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - - ngtcp2_ksl_remove(&gaptr->gap, NULL, &r); -} diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/lib/ngtcp2_gaptr.h deleted file mode 100644 index 9e7fa03086e..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_gaptr.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_GAPTR_H -#define NGTCP2_GAPTR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" -#include "ngtcp2_ksl.h" - -/* - * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX). - */ -typedef struct { - /* gap maintains the range of offset which is not received - yet. Initially, its range is [0, UINT64_MAX). */ - ngtcp2_ksl gap; - /* mem is custom memory allocator */ - const ngtcp2_mem *mem; -} ngtcp2_gaptr; - -/* - * ngtcp2_gaptr_init initializes |gaptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); - -/* - * ngtcp2_gaptr_free frees resources allocated for |gaptr|. - */ -void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr); - -/* - * ngtcp2_gaptr_push adds new data of length |datalen| at the stream - * offset |offset|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen); - -/* - * ngtcp2_gaptr_first_gap_offset returns the offset to the first gap. - * If there is no gap, it returns UINT64_MAX. - */ -uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr); - -/* - * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to - * the first gap which overlaps or comes after |offset|. - */ -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset); - -/* - * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset + - * datalen) is completely pushed into this object. - */ -int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, - size_t datalen); - -/* - * ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if - * the range is pushed. This function assumes that at least one gap - * exists. - */ -void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr); - -#endif /* NGTCP2_GAPTR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/lib/ngtcp2_idtr.c deleted file mode 100644 index f04806b4a8b..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_idtr.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_idtr.h" - -#include <assert.h> - -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { - int rv; - - rv = ngtcp2_gaptr_init(&idtr->gap, mem); - if (rv != 0) { - return rv; - } - - idtr->server = server; - - return 0; -} - -void ngtcp2_idtr_free(ngtcp2_idtr *idtr) { - if (idtr == NULL) { - return; - } - - ngtcp2_gaptr_free(&idtr->gap); -} - -/* - * id_from_stream_id translates |stream_id| to id space used by - * ngtcp2_idtr. - */ -static uint64_t id_from_stream_id(int64_t stream_id) { - return (uint64_t)(stream_id >> 2); -} - -int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id) { - uint64_t q; - - assert((idtr->server && (stream_id % 2)) || - (!idtr->server && (stream_id % 2)) == 0); - - q = id_from_stream_id(stream_id); - - if (ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1)) { - return NGTCP2_ERR_STREAM_IN_USE; - } - - return ngtcp2_gaptr_push(&idtr->gap, q, 1); -} - -int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id) { - uint64_t q; - - assert((idtr->server && (stream_id % 2)) || - (!idtr->server && (stream_id % 2)) == 0); - - q = id_from_stream_id(stream_id); - - return ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1); -} - -uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr) { - return ngtcp2_gaptr_first_gap_offset(&idtr->gap); -} diff --git a/deps/ngtcp2/lib/ngtcp2_idtr.h b/deps/ngtcp2/lib/ngtcp2_idtr.h deleted file mode 100644 index 63f332e64f9..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_idtr.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_IDTR_H -#define NGTCP2_IDTR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" -#include "ngtcp2_gaptr.h" - -/* - * ngtcp2_idtr tracks the usage of stream ID. - */ -typedef struct { - /* gap maintains the range of ID which is not used yet. Initially, - its range is [0, UINT64_MAX). */ - ngtcp2_gaptr gap; - /* server is nonzero if this object records server initiated stream - ID. */ - int server; -} ngtcp2_idtr; - -/* - * ngtcp2_idtr_init initializes |idtr|. - * - * If this object records server initiated ID (even number), set - * |server| to nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); - -/* - * ngtcp2_idtr_free frees resources allocated for |idtr|. - */ -void ngtcp2_idtr_free(ngtcp2_idtr *idtr); - -/* - * ngtcp2_idtr_open claims that |stream_id| is in used. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_STREAM_IN_USE - * ID has already been used. - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id); - -/* - * ngtcp2_idtr_open tells whether ID |stream_id| is in used or not. - * - * It returns nonzero if |stream_id| is used. - */ -int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id); - -/* - * ngtcp2_idtr_first_gap returns the first id of first gap. If there - * is no gap, it returns UINT64_MAX. The returned id is an id space - * used in this object internally, and not stream ID. - */ -uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr); - -#endif /* NGTCP2_IDTR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/lib/ngtcp2_ksl.c deleted file mode 100644 index 0c47150cb14..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_ksl.c +++ /dev/null @@ -1,715 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_ksl.h" - -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "ngtcp2_macro.h" -#include "ngtcp2_mem.h" -#include "ngtcp2_range.h" - -static size_t ksl_nodelen(size_t keylen) { - return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; -} - -static size_t ksl_blklen(size_t nodelen) { - return sizeof(ngtcp2_ksl_blk) + nodelen * NGTCP2_KSL_MAX_NBLK - - sizeof(uint64_t); -} - -/* - * ksl_node_set_key sets |key| to |node|. - */ -static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node, - const void *key) { - memcpy(node->key, key, ksl->keylen); -} - -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem) { - size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - ngtcp2_ksl_blk *head; - - ksl->head = ngtcp2_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGTCP2_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; - ksl->compar = compar; - ksl->keylen = keylen; - ksl->nodelen = nodelen; - ksl->n = 0; - ksl->mem = mem; - - head = ksl->head; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; - - return 0; -} - -/* - * ksl_free_blk frees |blk| recursively. - */ -static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { - size_t i; - - if (!blk->leaf) { - for (i = 0; i < blk->n; ++i) { - ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk); - } - } - - ngtcp2_mem_free(ksl->mem, blk); -} - -void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { - if (!ksl) { - return; - } - - ksl_free_blk(ksl, ksl->head); -} - -/* - * ksl_split_blk splits |blk| into 2 ngtcp2_ksl_blk objects. The new - * ngtcp2_ksl_blk is always the "right" block. - * - * It returns the pointer to the ngtcp2_ksl_blk created which is the - * located at the right of |blk|, or NULL which indicates out of - * memory error. - */ -static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { - ngtcp2_ksl_blk *rblk; - - rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); - if (rblk == NULL) { - return NULL; - } - - rblk->next = blk->next; - blk->next = rblk; - if (rblk->next) { - rblk->next->prev = rblk; - } else if (ksl->back == blk) { - ksl->back = rblk; - } - rblk->prev = blk; - rblk->leaf = blk->leaf; - - rblk->n = blk->n / 2; - - memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), - ksl->nodelen * rblk->n); - - blk->n -= rblk->n; - - assert(blk->n >= NGTCP2_KSL_MIN_NBLK); - assert(rblk->n >= NGTCP2_KSL_MIN_NBLK); - - return rblk; -} - -/* - * ksl_split_node splits a node included in |blk| at the position |i| - * into 2 adjacent nodes. The new node is always inserted at the - * position |i+1|. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { - ngtcp2_ksl_node *node; - ngtcp2_ksl_blk *lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk, *rblk; - - rblk = ksl_split_blk(ksl, lblk); - if (rblk == NULL) { - return NGTCP2_ERR_NOMEM; - } - - memmove(blk->nodes + (i + 2) * ksl->nodelen, - blk->nodes + (i + 1) * ksl->nodelen, - ksl->nodelen * (blk->n - (i + 1))); - - node = ngtcp2_ksl_nth_node(ksl, blk, i + 1); - node->blk = rblk; - ++blk->n; - ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); - - node = ngtcp2_ksl_nth_node(ksl, blk, i); - ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - - return 0; -} - -/* - * ksl_split_head splits a head (root) block. It increases the height - * of skip list by 1. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int ksl_split_head(ngtcp2_ksl *ksl) { - ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; - ngtcp2_ksl_node *node; - - rblk = ksl_split_blk(ksl, ksl->head); - if (rblk == NULL) { - return NGTCP2_ERR_NOMEM; - } - - lblk = ksl->head; - - nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); - if (nhead == NULL) { - ngtcp2_mem_free(ksl->mem, rblk); - return NGTCP2_ERR_NOMEM; - } - nhead->next = nhead->prev = NULL; - nhead->n = 2; - nhead->leaf = 0; - - node = ngtcp2_ksl_nth_node(ksl, nhead, 0); - ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - node->blk = lblk; - - node = ngtcp2_ksl_nth_node(ksl, nhead, 1); - ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); - node->blk = rblk; - - ksl->head = nhead; - - return 0; -} - -/* - * insert_node inserts a node whose key is |key| with the associated - * |data| at the index of |i|. This function assumes that the number - * of nodes contained by |blk| is strictly less than - * NGTCP2_KSL_MAX_NBLK. - */ -static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i, - const ngtcp2_ksl_key *key, void *data) { - ngtcp2_ksl_node *node; - - assert(blk->n < NGTCP2_KSL_MAX_NBLK); - - memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, - ksl->nodelen * (blk->n - i)); - - node = ngtcp2_ksl_nth_node(ksl, blk, i); - ksl_node_set_key(ksl, node, key); - node->data = data; - - ++blk->n; -} - -static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, - const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) { - ngtcp2_ssize left = -1, right = (ngtcp2_ssize)blk->n, mid; - ngtcp2_ksl_node *node; - - while (right - left > 1) { - mid = (left + right) / 2; - node = ngtcp2_ksl_nth_node(ksl, blk, (size_t)mid); - if (compar((ngtcp2_ksl_key *)node->key, key)) { - left = mid; - } else { - right = mid; - } - } - - return (size_t)right; -} - -int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, - const ngtcp2_ksl_key *key, void *data) { - ngtcp2_ksl_blk *blk = ksl->head; - ngtcp2_ksl_node *node; - size_t i; - int rv; - - if (blk->n == NGTCP2_KSL_MAX_NBLK) { - rv = ksl_split_head(ksl); - if (rv != 0) { - return rv; - } - blk = ksl->head; - } - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (blk->leaf) { - if (i < blk->n && - !ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) { - if (it) { - *it = ngtcp2_ksl_end(ksl); - } - return NGTCP2_ERR_INVALID_ARGUMENT; - } - ksl_insert_node(ksl, blk, i, key, data); - ++ksl->n; - if (it) { - ngtcp2_ksl_it_init(it, ksl, blk, i); - } - return 0; - } - - if (i == blk->n) { - /* This insertion extends the largest key in this subtree. */ - for (; !blk->leaf;) { - node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1); - if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { - rv = ksl_split_node(ksl, blk, blk->n - 1); - if (rv != 0) { - return rv; - } - node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1); - } - ksl_node_set_key(ksl, node, key); - blk = node->blk; - } - ksl_insert_node(ksl, blk, blk->n, key, data); - ++ksl->n; - if (it) { - ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1); - } - return 0; - } - - node = ngtcp2_ksl_nth_node(ksl, blk, i); - - if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { - rv = ksl_split_node(ksl, blk, i); - if (rv != 0) { - return rv; - } - if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) { - node = ngtcp2_ksl_nth_node(ksl, blk, i + 1); - if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) { - ksl_node_set_key(ksl, node, key); - } - } - } - - blk = node->blk; - } -} - -/* - * ksl_remove_node removes the node included in |blk| at the index of - * |i|. - */ -static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { - memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, - ksl->nodelen * (blk->n - (i + 1))); - - --blk->n; -} - -/* - * ksl_merge_node merges 2 nodes which are the nodes at the index of - * |i| and |i + 1|. - * - * If |blk| is the direct descendant of head (root) block and the head - * block contains just 2 nodes, the merged block becomes head block, - * which decreases the height of |ksl| by 1. - * - * This function returns the pointer to the merged block. - */ -static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, - size_t i) { - ngtcp2_ksl_blk *lblk, *rblk; - - assert(i + 1 < blk->n); - - lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk; - rblk = ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk; - - assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK); - - memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, - ksl->nodelen * rblk->n); - - lblk->n += rblk->n; - lblk->next = rblk->next; - if (lblk->next) { - lblk->next->prev = lblk; - } else if (ksl->back == rblk) { - ksl->back = lblk; - } - - ngtcp2_mem_free(ksl->mem, rblk); - - if (ksl->head == blk && blk->n == 2) { - ngtcp2_mem_free(ksl->mem, ksl->head); - ksl->head = lblk; - } else { - ksl_remove_node(ksl, blk, i + 1); - ksl_node_set_key(ksl, ngtcp2_ksl_nth_node(ksl, blk, i), - ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - } - - return lblk; -} - -/* - * ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to - * blk->nodes[i - 1]->blk->nodes. - */ -static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { - ngtcp2_ksl_node *lnode, *rnode, *dest, *src; - - assert(i > 0); - - lnode = ngtcp2_ksl_nth_node(ksl, blk, i - 1); - rnode = ngtcp2_ksl_nth_node(ksl, blk, i); - - assert(lnode->blk->n < NGTCP2_KSL_MAX_NBLK); - assert(rnode->blk->n > NGTCP2_KSL_MIN_NBLK); - - dest = ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n); - src = ngtcp2_ksl_nth_node(ksl, rnode->blk, 0); - - memcpy(dest, src, ksl->nodelen); - ksl_node_set_key(ksl, lnode, dest->key); - ++lnode->blk->n; - - --rnode->blk->n; - memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen, - ksl->nodelen * rnode->blk->n); -} - -/* - * ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to - * blk->nodes[i + 1]->blk->nodes. - */ -static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { - ngtcp2_ksl_node *lnode, *rnode, *dest, *src; - - assert(i < blk->n - 1); - - lnode = ngtcp2_ksl_nth_node(ksl, blk, i); - rnode = ngtcp2_ksl_nth_node(ksl, blk, i + 1); - - assert(lnode->blk->n > NGTCP2_KSL_MIN_NBLK); - assert(rnode->blk->n < NGTCP2_KSL_MAX_NBLK); - - memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes, - ksl->nodelen * rnode->blk->n); - ++rnode->blk->n; - - dest = ngtcp2_ksl_nth_node(ksl, rnode->blk, 0); - src = ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); - - memcpy(dest, src, ksl->nodelen); - - --lnode->blk->n; - ksl_node_set_key( - ksl, lnode, ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); -} - -/* - * key_equal returns nonzero if |lhs| and |rhs| are equal using the - * function |compar|. - */ -static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs, - const ngtcp2_ksl_key *rhs) { - return !compar(lhs, rhs) && !compar(rhs, lhs); -} - -int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, - const ngtcp2_ksl_key *key) { - ngtcp2_ksl_blk *blk = ksl->head; - ngtcp2_ksl_node *node; - size_t i; - - if (!blk->leaf && blk->n == 2 && - ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && - ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) { - blk = ksl_merge_node(ksl, ksl->head, 0); - } - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (i == blk->n) { - if (it) { - *it = ngtcp2_ksl_end(ksl); - } - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - if (blk->leaf) { - if (ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) { - if (it) { - *it = ngtcp2_ksl_end(ksl); - } - return NGTCP2_ERR_INVALID_ARGUMENT; - } - ksl_remove_node(ksl, blk, i); - --ksl->n; - if (it) { - if (blk->n == i && blk->next) { - ngtcp2_ksl_it_init(it, ksl, blk->next, 0); - } else { - ngtcp2_ksl_it_init(it, ksl, blk, i); - } - } - return 0; - } - - node = ngtcp2_ksl_nth_node(ksl, blk, i); - - if (node->blk->n == NGTCP2_KSL_MIN_NBLK) { - if (i > 0 && - ngtcp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { - ksl_shift_right(ksl, blk, i - 1); - blk = node->blk; - } else if (i + 1 < blk->n && - ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > - NGTCP2_KSL_MIN_NBLK) { - ksl_shift_left(ksl, blk, i + 1); - blk = node->blk; - } else if (i > 0) { - blk = ksl_merge_node(ksl, blk, i - 1); - } else { - assert(i + 1 < blk->n); - blk = ksl_merge_node(ksl, blk, i); - } - } else { - blk = node->blk; - } - } -} - -ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, - const ngtcp2_ksl_key *key) { - ngtcp2_ksl_blk *blk = ksl->head; - ngtcp2_ksl_it it; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (blk->leaf) { - if (i == blk->n && blk->next) { - blk = blk->next; - i = 0; - } - ngtcp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - - if (i == blk->n) { - /* This happens if descendant has smaller key. Fast forward to - find last node in this subtree. */ - for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) - ; - if (blk->next) { - blk = blk->next; - i = 0; - } else { - i = blk->n; - } - ngtcp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk; - } -} - -ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, - const ngtcp2_ksl_key *key, - ngtcp2_ksl_compar compar) { - ngtcp2_ksl_blk *blk = ksl->head; - ngtcp2_ksl_it it; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, key, compar); - - if (blk->leaf) { - if (i == blk->n && blk->next) { - blk = blk->next; - i = 0; - } - ngtcp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - - if (i == blk->n) { - /* This happens if descendant has smaller key. Fast forward to - find last node in this subtree. */ - for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) - ; - if (blk->next) { - blk = blk->next; - i = 0; - } else { - i = blk->n; - } - ngtcp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk; - } -} - -void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, - const ngtcp2_ksl_key *new_key) { - ngtcp2_ksl_blk *blk = ksl->head; - ngtcp2_ksl_node *node; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, old_key, ksl->compar); - - assert(i < blk->n); - node = ngtcp2_ksl_nth_node(ksl, blk, i); - - if (blk->leaf) { - assert(key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key)); - ksl_node_set_key(ksl, node, new_key); - return; - } - - if (key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key) || - ksl->compar((ngtcp2_ksl_key *)node->key, new_key)) { - ksl_node_set_key(ksl, node, new_key); - } - - blk = node->blk; - } -} - -static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) { - size_t i; - ngtcp2_ksl_node *node; - - fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); - - if (blk->leaf) { - for (i = 0; i < blk->n; ++i) { - node = ngtcp2_ksl_nth_node(ksl, blk, i); - fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); - } - fprintf(stderr, "\n"); - return; - } - - for (i = 0; i < blk->n; ++i) { - ksl_print(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk, level + 1); - } -} - -size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; } - -void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) { - size_t i; - ngtcp2_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk); - } - } - - ksl->front = ksl->back = ksl->head; - ksl->n = 0; - - head = ksl->head; - - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; -} - -void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } - -ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) { - ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); - return it; -} - -ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) { - ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); - return it; -} - -void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, - ngtcp2_ksl_blk *blk, size_t i) { - it->ksl = ksl; - it->blk = blk; - it->i = i; -} - -void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) { - assert(it->i < it->blk->n); - return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; -} - -void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) { - assert(!ngtcp2_ksl_it_begin(it)); - - if (it->i == 0) { - it->blk = it->blk->prev; - it->i = it->blk->n - 1; - } else { - --it->i; - } -} - -int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) { - return it->i == 0 && it->blk->prev == NULL; -} - -int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, - const ngtcp2_ksl_key *rhs) { - const ngtcp2_range *a = lhs, *b = rhs; - return a->begin < b->begin; -} - -int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, - const ngtcp2_ksl_key *rhs) { - const ngtcp2_range *a = lhs, *b = rhs; - return a->begin < b->begin && - !(ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end)); -} diff --git a/deps/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/lib/ngtcp2_ksl.h deleted file mode 100644 index 071e10dce1b..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_ksl.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_KSL_H -#define NGTCP2_KSL_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <stdlib.h> - -#include <ngtcp2/ngtcp2.h> - -/* - * Skip List using single key instead of range. - */ - -#define NGTCP2_KSL_DEGR 16 -/* NGTCP2_KSL_MAX_NBLK is the maximum number of nodes which a single - block can contain. */ -#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR - 1) -/* NGTCP2_KSL_MIN_NBLK is the minimum number of nodes which a single - block other than root must contains. */ -#define NGTCP2_KSL_MIN_NBLK (NGTCP2_KSL_DEGR - 1) - -/* - * ngtcp2_ksl_key represents key in ngtcp2_ksl. - */ -typedef void ngtcp2_ksl_key; - -struct ngtcp2_ksl_node; -typedef struct ngtcp2_ksl_node ngtcp2_ksl_node; - -struct ngtcp2_ksl_blk; -typedef struct ngtcp2_ksl_blk ngtcp2_ksl_blk; - -/* - * ngtcp2_ksl_node is a node which contains either ngtcp2_ksl_blk or - * opaque data. If a node is an internal node, it contains - * ngtcp2_ksl_blk. Otherwise, it has data. The key is stored at the - * location starting at key. - */ -struct ngtcp2_ksl_node { - union { - ngtcp2_ksl_blk *blk; - void *data; - }; - union { - uint64_t align; - /* key is a buffer to include key associated to this node. - Because the length of key is unknown until ngtcp2_ksl_init is - called, the actual buffer will be allocated after this - field. */ - uint8_t key[1]; - }; -}; - -/* - * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects. - */ -struct ngtcp2_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - ngtcp2_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - ngtcp2_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - size_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - int leaf; - union { - uint64_t align; - /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK - ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until ngtcp2_ksl_init is - called. */ - uint8_t nodes[1]; - }; -}; - -/* - * ngtcp2_ksl_compar is a function type which returns nonzero if key - * |lhs| should be placed before |rhs|. It returns 0 otherwise. - */ -typedef int (*ngtcp2_ksl_compar)(const ngtcp2_ksl_key *lhs, - const ngtcp2_ksl_key *rhs); - -struct ngtcp2_ksl; -typedef struct ngtcp2_ksl ngtcp2_ksl; - -struct ngtcp2_ksl_it; -typedef struct ngtcp2_ksl_it ngtcp2_ksl_it; - -/* - * ngtcp2_ksl_it is a forward iterator to iterate nodes. - */ -struct ngtcp2_ksl_it { - const ngtcp2_ksl *ksl; - ngtcp2_ksl_blk *blk; - size_t i; -}; - -/* - * ngtcp2_ksl is a deterministic paged skip list. - */ -struct ngtcp2_ksl { - /* head points to the root block. */ - ngtcp2_ksl_blk *head; - /* front points to the first leaf block. */ - ngtcp2_ksl_blk *front; - /* back points to the last leaf block. */ - ngtcp2_ksl_blk *back; - ngtcp2_ksl_compar compar; - size_t n; - /* keylen is the size of key */ - size_t keylen; - /* nodelen is the actual size of ngtcp2_ksl_node including key - storage. */ - size_t nodelen; - const ngtcp2_mem *mem; -}; - -/* - * ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare - * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem); - -/* - * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is - * NULL, this function does nothing. It does not free the memory - * region pointed by |ksl| itself. - */ -void ngtcp2_ksl_free(ngtcp2_ksl *ksl); - -/* - * ngtcp2_ksl_insert inserts |key| with its associated |data|. On - * successful insertion, the iterator points to the inserted node is - * stored in |*it|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_INVALID_ARGUMENT - * |key| already exists. - */ -int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, - const ngtcp2_ksl_key *key, void *data); - -/* - * ngtcp2_ksl_remove removes the |key| from |ksl|. - * - * This function assigns the iterator to |*it|, which points to the - * node which is located at the right next of the removed node if |it| - * is not NULL. If |key| is not found, no deletion takes place and - * the return value of ngtcp2_ksl_end(ksl) is assigned to |*it|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * |key| does not exist. - */ -int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, - const ngtcp2_ksl_key *key); - -/* - * ngtcp2_ksl_lower_bound returns the iterator which points to the - * first node which has the key which is equal to |key| or the last - * node which satisfies !compar(&node->key, key). If there is no such - * node, it returns the iterator which satisfies ngtcp2_ksl_it_end(it) - * != 0. - */ -ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, - const ngtcp2_ksl_key *key); - -/* - * ngtcp2_ksl_lower_bound_compar works like ngtcp2_ksl_lower_bound, - * but it takes custom function |compar| to do lower bound search. - */ -ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, - const ngtcp2_ksl_key *key, - ngtcp2_ksl_compar compar); - -/* - * ngtcp2_ksl_update_key replaces the key of nodes which has |old_key| - * with |new_key|. |new_key| must be strictly greater than the - * previous node and strictly smaller than the next node. - */ -void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, - const ngtcp2_ksl_key *new_key); - -/* - * ngtcp2_ksl_begin returns the iterator which points to the first - * node. If there is no node in |ksl|, it returns the iterator which - * satisfies ngtcp2_ksl_it_end(it) != 0. - */ -ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl); - -/* - * ngtcp2_ksl_end returns the iterator which points to the node - * following the last node. The returned object satisfies - * ngtcp2_ksl_it_end(). If there is no node in |ksl|, it returns the - * iterator which satisfies ngtcp2_ksl_it_begin(it) != 0. - */ -ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl); - -/* - * ngtcp2_ksl_len returns the number of elements stored in |ksl|. - */ -size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl); - -/* - * ngtcp2_ksl_clear removes all elements stored in |ksl|. - */ -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))) - -/* - * ngtcp2_ksl_print prints its internal state in stderr. It assumes - * that the key is of type int64_t. This function should be used for - * the debugging purpose only. - */ -void ngtcp2_ksl_print(ngtcp2_ksl *ksl); - -/* - * ngtcp2_ksl_it_init initializes |it|. - */ -void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, - ngtcp2_ksl_blk *blk, size_t i); - -/* - * ngtcp2_ksl_it_get returns the data associated to the node which - * |it| points to. It is undefined to call this function when - * ngtcp2_ksl_it_end(it) returns nonzero. - */ -void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it); - -/* - * 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) - -/* - * ngtcp2_ksl_it_prev moves backward the iterator by one. It is - * undefined if this function is called when ngtcp2_ksl_it_begin(it) - * returns nonzero. - */ -void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it); - -/* - * ngtcp2_ksl_it_end returns nonzero if |it| points to the beyond the - * last node. - */ -#define ngtcp2_ksl_it_end(IT) \ - ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) - -/* - * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first - * node. |it| might satisfy both ngtcp2_ksl_it_begin(&it) and - * ngtcp2_ksl_it_end(&it) if the skip list has no node. - */ -int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it); - -/* - * ngtcp2_ksl_key returns the key of the node which |it| points to. - * 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) - -/* - * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar. - * lhs->ptr and rhs->ptr must point to ngtcp2_range object and the - * function returns nonzero if (const ngtcp2_range *)(lhs->ptr)->begin - * < (const ngtcp2_range *)(rhs->ptr)->begin. - */ -int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, - const ngtcp2_ksl_key *rhs); - -/* - * ngtcp2_ksl_range_exclusive_compar is an implementation of - * ngtcp2_ksl_compar. lhs->ptr and rhs->ptr must point to - * ngtcp2_range object and the function returns nonzero if (const - * ngtcp2_range *)(lhs->ptr)->begin < (const ngtcp2_range - * *)(rhs->ptr)->begin and the 2 ranges do not intersect. - */ -int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, - const ngtcp2_ksl_key *rhs); - -#endif /* NGTCP2_KSL_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/lib/ngtcp2_log.c deleted file mode 100644 index 8b7ec0def65..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_log.c +++ /dev/null @@ -1,747 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_log.h" - -#include <stdio.h> -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif -#include <assert.h> -#include <string.h> - -#include "ngtcp2_str.h" -#include "ngtcp2_vec.h" -#include "ngtcp2_macro.h" - -void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, - ngtcp2_printf log_printf, ngtcp2_tstamp ts, - void *user_data) { - if (scid) { - ngtcp2_encode_hex(log->scid, scid->data, scid->datalen); - } else { - log->scid[0] = '\0'; - } - log->log_printf = log_printf; - log->ts = log->last_ts = ts; - log->user_data = user_data; -} - -/* - * # Log header - * - * <LEVEL><TIMESTAMP> <SCID> <EVENT> - * - * <LEVEL>: - * Log level. I=Info, W=Warning, E=Error - * - * <TIMESTAMP>: - * Timestamp relative to ngtcp2_log.ts field in milliseconds - * resolution. - * - * <SCID>: - * Source Connection ID in hex string. - * - * <EVENT>: - * Event. pkt=packet, frm=frame, rcv=recovery, cry=crypto, - * con=connection(catch all) - * - * # Frame event - * - * <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>) - * - * <DIR>: - * Flow direction. tx=transmission, rx=reception - * - * <PKN>: - * Packet number. - * - * <PKTNAME>: - * Packet name. (e.g., Initial, Handshake, S01) - * - * <PKTTYPE>: - * Packet type in hex string. - * - * <FRAMENAME>: - * Frame name. (e.g., STREAM, ACK, PING) - * - * <FRAMETYPE>: - * Frame type in hex string. - */ - -#define NGTCP2_LOG_BUFLEN 4096 - -/* TODO Split second and remaining fraction with comma */ -#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s" -#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)" -#define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters" - -#define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \ - timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type - -#define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \ - timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type - -#define NGTCP2_LOG_TP_HD_FIELDS \ - timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry" - -static const char *strerrorcode(uint64_t error_code) { - switch (error_code) { - case NGTCP2_NO_ERROR: - return "NO_ERROR"; - case NGTCP2_INTERNAL_ERROR: - return "INTERNAL_ERROR"; - case NGTCP2_CONNECTION_REFUSED: - return "CONNECTION_REFUSED"; - case NGTCP2_FLOW_CONTROL_ERROR: - return "FLOW_CONTROL_ERROR"; - case NGTCP2_STREAM_LIMIT_ERROR: - return "STREAM_LIMIT_ERROR"; - case NGTCP2_STREAM_STATE_ERROR: - return "STREAM_STATE_ERROR"; - case NGTCP2_FINAL_SIZE_ERROR: - return "FINAL_SIZE_ERROR"; - case NGTCP2_FRAME_ENCODING_ERROR: - return "FRAME_ENCODING_ERROR"; - case NGTCP2_TRANSPORT_PARAMETER_ERROR: - return "TRANSPORT_PARAMETER_ERROR"; - case NGTCP2_CONNECTION_ID_LIMIT_ERROR: - return "CONNECTION_ID_LIMIT_ERROR"; - case NGTCP2_PROTOCOL_VIOLATION: - return "PROTOCOL_VIOLATION"; - case NGTCP2_INVALID_TOKEN: - return "INVALID_TOKEN"; - case NGTCP2_APPLICATION_ERROR: - return "APPLICATION_ERROR"; - case NGTCP2_CRYPTO_BUFFER_EXCEEDED: - return "CRYPTO_BUFFER_EXCEEDED"; - case NGTCP2_KEY_UPDATE_ERROR: - return "KEY_UPDATE_ERROR"; - default: - if (0x100u <= error_code && error_code <= 0x1ffu) { - return "CRYPTO_ERROR"; - } - return "(unknown)"; - } -} - -static const char *strapperrorcode(uint64_t app_error_code) { - (void)app_error_code; - return "(unknown)"; -} - -static const char *strpkttype_long(uint8_t type) { - switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: - return "VN"; - case NGTCP2_PKT_INITIAL: - return "Initial"; - case NGTCP2_PKT_RETRY: - return "Retry"; - case NGTCP2_PKT_HANDSHAKE: - return "Handshake"; - case NGTCP2_PKT_0RTT: - return "0RTT"; - default: - return "(unknown)"; - } -} - -static const char *strpkttype(const ngtcp2_pkt_hd *hd) { - if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { - return strpkttype_long(hd->type); - } - return "Short"; -} - -static const char *strevent(ngtcp2_log_event ev) { - switch (ev) { - case NGTCP2_LOG_EVENT_CON: - return "con"; - case NGTCP2_LOG_EVENT_PKT: - return "pkt"; - case NGTCP2_LOG_EVENT_FRM: - return "frm"; - case NGTCP2_LOG_EVENT_RCV: - return "rcv"; - case NGTCP2_LOG_EVENT_CRY: - return "cry"; - case NGTCP2_LOG_EVENT_PTV: - return "ptv"; - case NGTCP2_LOG_EVENT_NONE: - default: - return "non"; - } -} - -static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; } - -static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_stream *fr, const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 - " fin=%d offset=%" PRIu64 " len=%zu uni=%d"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, - fr->stream_id, fr->fin, fr->offset, - ngtcp2_vec_len(fr->data, fr->datacnt), - (fr->stream_id & 0x2) != 0); -} - -static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_ack *fr, const char *dir) { - int64_t largest_ack, min_ack; - size_t i; - - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " ACK(0x%02x) largest_ack=%" PRId64 - " ack_delay=%" PRIu64 "(%" PRIu64 - ") ack_block_count=%zu"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->largest_ack, - fr->ack_delay_unscaled / NGTCP2_MILLISECONDS, fr->ack_delay, - fr->num_blks); - - largest_ack = fr->largest_ack; - min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; - - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64 - "] block_count=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, min_ack, - fr->first_ack_blklen); - - for (i = 0; i < fr->num_blks; ++i) { - const ngtcp2_ack_blk *blk = &fr->blks[i]; - largest_ack = min_ack - (int64_t)blk->gap - 2; - min_ack = largest_ack - (int64_t)blk->blklen; - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64 - "] gap=%" PRIu64 " block_count=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, - min_ack, blk->gap, blk->blklen); - } -} - -static void log_fr_padding(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_padding *fr, const char *dir) { - log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PADDING(0x%02x) len=%zu"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->len); -} - -static void log_fr_reset_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_reset_stream *fr, - const char *dir) { - log->log_printf( - log->user_data, - (NGTCP2_LOG_PKT " RESET_STREAM(0x%02x) id=0x%" PRIx64 - " app_error_code=%s(0x%" PRIx64 ") final_size=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, - strapperrorcode(fr->app_error_code), fr->app_error_code, fr->final_size); -} - -static void log_fr_connection_close(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_connection_close *fr, - const char *dir) { - char reason[256]; - size_t reasonlen = ngtcp2_min(sizeof(reason) - 1, fr->reasonlen); - - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT - " CONNECTION_CLOSE(0x%02x) error_code=%s(0x%" PRIx64 ") " - "frame_type=%" PRIx64 " reason_len=%zu reason=[%s]"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, - fr->type == NGTCP2_FRAME_CONNECTION_CLOSE - ? strerrorcode(fr->error_code) - : strapperrorcode(fr->error_code), - fr->error_code, fr->frame_type, fr->reasonlen, - ngtcp2_encode_printable_ascii(reason, fr->reason, reasonlen)); -} - -static void log_fr_max_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_max_data *fr, const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " MAX_DATA(0x%02x) max_data=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_data); -} - -static void log_fr_max_stream_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_max_stream_data *fr, - const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " MAX_STREAM_DATA(0x%02x) id=0x%" PRIx64 - " max_stream_data=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, - fr->max_stream_data); -} - -static void log_fr_max_streams(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_max_streams *fr, const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " MAX_STREAMS(0x%02x) max_streams=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams); -} - -static void log_fr_ping(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_ping *fr, const char *dir) { - log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PING(0x%02x)"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); -} - -static void log_fr_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_data_blocked *fr, - const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " DATA_BLOCKED(0x%02x) offset=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset); -} - -static void log_fr_stream_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_stream_data_blocked *fr, - const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " STREAM_DATA_BLOCKED(0x%02x) id=0x%" PRIx64 - " offset=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, - fr->offset); -} - -static void log_fr_streams_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_streams_blocked *fr, - const char *dir) { - log->log_printf( - log->user_data, - (NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02x) stream_limit=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_limit); -} - -static void log_fr_new_connection_id(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_new_connection_id *fr, - const char *dir) { - uint8_t buf[sizeof(fr->stateless_reset_token) * 2 + 1]; - uint8_t cid[sizeof(fr->cid.data) * 2 + 1]; - - log->log_printf( - log->user_data, - (NGTCP2_LOG_PKT " NEW_CONNECTION_ID(0x%02x) seq=%" PRIu64 - " cid=0x%s retire_prior_to=%" PRIu64 - " stateless_reset_token=0x%s"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq, - (const char *)ngtcp2_encode_hex(cid, fr->cid.data, fr->cid.datalen), - fr->retire_prior_to, - (const char *)ngtcp2_encode_hex(buf, fr->stateless_reset_token, - sizeof(fr->stateless_reset_token))); -} - -static void log_fr_stop_sending(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_stop_sending *fr, - const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " STOP_SENDING(0x%02x) id=0x%" PRIx64 - " app_error_code=%s(0x%" PRIx64 ")"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, - strapperrorcode(fr->app_error_code), fr->app_error_code); -} - -static void log_fr_path_challenge(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_path_challenge *fr, - const char *dir) { - uint8_t buf[sizeof(fr->data) * 2 + 1]; - - log->log_printf( - log->user_data, (NGTCP2_LOG_PKT " PATH_CHALLENGE(0x%02x) data=0x%s"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, - (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data))); -} - -static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_path_response *fr, - const char *dir) { - uint8_t buf[sizeof(fr->data) * 2 + 1]; - - log->log_printf( - log->user_data, (NGTCP2_LOG_PKT " PATH_RESPONSE(0x%02x) data=0x%s"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, - (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data))); -} - -static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_crypto *fr, const char *dir) { - size_t datalen = 0; - size_t i; - - for (i = 0; i < fr->datacnt; ++i) { - datalen += fr->data[i].len; - } - - log->log_printf( - log->user_data, - (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen); -} - -static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_new_token *fr, const char *dir) { - /* Show at most first 64 bytes of token. If token is longer than 64 - bytes, log first 64 bytes and then append "*" */ - uint8_t buf[128 + 1 + 1]; - uint8_t *p; - - if (fr->token.len > 64) { - p = ngtcp2_encode_hex(buf, fr->token.base, 64); - p[128] = '*'; - p[129] = '\0'; - } else { - p = ngtcp2_encode_hex(buf, fr->token.base, fr->token.len); - } - log->log_printf( - log->user_data, (NGTCP2_LOG_PKT " NEW_TOKEN(0x%02x) token=0x%s len=%zu"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, (const char *)p, fr->token.len); -} - -static void log_fr_retire_connection_id(ngtcp2_log *log, - const ngtcp2_pkt_hd *hd, - const ngtcp2_retire_connection_id *fr, - const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " RETIRE_CONNECTION_ID(0x%02x) seq=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq); -} - -static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_handshake_done *fr, - const char *dir) { - log->log_printf(log->user_data, (NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02x)"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); -} - -static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_frame *fr, const char *dir) { - switch (fr->type) { - case NGTCP2_FRAME_STREAM: - log_fr_stream(log, hd, &fr->stream, dir); - break; - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - log_fr_ack(log, hd, &fr->ack, dir); - break; - case NGTCP2_FRAME_PADDING: - log_fr_padding(log, hd, &fr->padding, dir); - break; - case NGTCP2_FRAME_RESET_STREAM: - log_fr_reset_stream(log, hd, &fr->reset_stream, dir); - break; - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - log_fr_connection_close(log, hd, &fr->connection_close, dir); - break; - case NGTCP2_FRAME_MAX_DATA: - log_fr_max_data(log, hd, &fr->max_data, dir); - break; - case NGTCP2_FRAME_MAX_STREAM_DATA: - log_fr_max_stream_data(log, hd, &fr->max_stream_data, dir); - break; - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - case NGTCP2_FRAME_MAX_STREAMS_UNI: - log_fr_max_streams(log, hd, &fr->max_streams, dir); - break; - case NGTCP2_FRAME_PING: - log_fr_ping(log, hd, &fr->ping, dir); - break; - case NGTCP2_FRAME_DATA_BLOCKED: - log_fr_data_blocked(log, hd, &fr->data_blocked, dir); - break; - case NGTCP2_FRAME_STREAM_DATA_BLOCKED: - log_fr_stream_data_blocked(log, hd, &fr->stream_data_blocked, dir); - break; - case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: - case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: - log_fr_streams_blocked(log, hd, &fr->streams_blocked, dir); - break; - case NGTCP2_FRAME_NEW_CONNECTION_ID: - log_fr_new_connection_id(log, hd, &fr->new_connection_id, dir); - break; - case NGTCP2_FRAME_STOP_SENDING: - log_fr_stop_sending(log, hd, &fr->stop_sending, dir); - break; - case NGTCP2_FRAME_PATH_CHALLENGE: - log_fr_path_challenge(log, hd, &fr->path_challenge, dir); - break; - case NGTCP2_FRAME_PATH_RESPONSE: - log_fr_path_response(log, hd, &fr->path_response, dir); - break; - case NGTCP2_FRAME_CRYPTO: - log_fr_crypto(log, hd, &fr->crypto, dir); - break; - case NGTCP2_FRAME_NEW_TOKEN: - log_fr_new_token(log, hd, &fr->new_token, dir); - break; - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - log_fr_retire_connection_id(log, hd, &fr->retire_connection_id, dir); - break; - case NGTCP2_FRAME_HANDSHAKE_DONE: - log_fr_handshake_done(log, hd, &fr->handshake_done, dir); - break; - default: - assert(0); - } -} - -void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_frame *fr) { - if (!log->log_printf) { - return; - } - - log_fr(log, hd, fr, "rx"); -} - -void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_frame *fr) { - if (!log->log_printf) { - return; - } - - log_fr(log, hd, fr, "tx"); -} - -void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const uint32_t *sv, size_t nsv) { - size_t i; - - if (!log->log_printf) { - return; - } - - for (i = 0; i < nsv; ++i) { - log->log_printf(log->user_data, (NGTCP2_LOG_PKT " v=0x%08x"), - NGTCP2_LOG_PKT_HD_FIELDS("rx"), sv[i]); - } -} - -void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { - uint8_t buf[sizeof(sr->stateless_reset_token) * 2 + 1]; - ngtcp2_pkt_hd shd; - ngtcp2_pkt_hd *hd = &shd; - - if (!log->log_printf) { - return; - } - - memset(&shd, 0, sizeof(shd)); - - log->log_printf( - log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"), - NGTCP2_LOG_PKT_HD_FIELDS("rx"), - (const char *)ngtcp2_encode_hex(buf, sr->stateless_reset_token, - sizeof(sr->stateless_reset_token)), - sr->randlen); -} - -void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, - const ngtcp2_transport_params *params) { - uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1]; - uint8_t addr[16 * 2 + 1]; - uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1]; - - if (!log->log_printf) { - return; - } - - if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { - if (params->stateless_reset_token_present) { - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " stateless_reset_token=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex( - token, params->stateless_reset_token, - sizeof(params->stateless_reset_token))); - } - - if (params->preferred_address_present) { - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " preferred_address.ipv4_addr=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex( - addr, params->preferred_address.ipv4_addr, - sizeof(params->preferred_address.ipv4_addr))); - log->log_printf( - log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv4_port=%u"), - NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv4_port); - - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " preferred_address.ipv6_addr=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex( - addr, params->preferred_address.ipv6_addr, - sizeof(params->preferred_address.ipv6_addr))); - log->log_printf( - log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv6_port=%u"), - NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv6_port); - - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " preferred_address.cid=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex( - cid, params->preferred_address.cid.data, - params->preferred_address.cid.datalen)); - log->log_printf( - log->user_data, - (NGTCP2_LOG_TP " preferred_address.stateless_reset_token=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex( - token, params->preferred_address.stateless_reset_token, - sizeof(params->preferred_address.stateless_reset_token))); - } - - log->log_printf( - log->user_data, - (NGTCP2_LOG_TP " original_destination_connection_id=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex(cid, params->original_dcid.data, - params->original_dcid.datalen)); - - if (params->retry_scid_present) { - log->log_printf( - log->user_data, (NGTCP2_LOG_TP " retry_source_connection_id=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex(cid, params->retry_scid.data, - params->retry_scid.datalen)); - } - } - - log->log_printf( - log->user_data, (NGTCP2_LOG_TP " initial_source_connection_id=0x%s"), - NGTCP2_LOG_TP_HD_FIELDS, - (const char *)ngtcp2_encode_hex(cid, params->initial_scid.data, - params->initial_scid.datalen)); - - log->log_printf( - log->user_data, - (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_local); - log->log_printf( - log->user_data, - (NGTCP2_LOG_TP " initial_max_stream_data_bidi_remote=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_remote); - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " initial_max_stream_data_uni=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni); - log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data); - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " initial_max_bidi_streams=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi); - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " initial_max_uni_streams=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni); - log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, - params->max_idle_timeout / NGTCP2_MILLISECONDS); - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " max_udp_payload_size=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->max_udp_payload_size); - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " ack_delay_exponent=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent); - log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, - params->max_ack_delay / NGTCP2_MILLISECONDS); - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64), - NGTCP2_LOG_TP_HD_FIELDS, params->active_connection_id_limit); - log->log_printf(log->user_data, - (NGTCP2_LOG_TP " disable_active_migration=%d"), - NGTCP2_LOG_TP_HD_FIELDS, params->disable_active_migration); -} - -void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, - uint8_t flags, ngtcp2_tstamp sent_ts) { - if (!log->log_printf) { - return; - } - - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num, - (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short", - type, sent_ts); -} - -static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const char *dir) { - uint8_t dcid[sizeof(hd->dcid.data) * 2 + 1]; - uint8_t scid[sizeof(hd->scid.data) * 2 + 1]; - - if (!log->log_printf) { - return; - } - - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_PKT, - "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d", - dir, hd->pkt_num, - (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), - (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), - (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type) - : "Short", - hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); -} - -void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { - log_pkt_hd(log, hd, "rx"); -} - -void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { - log_pkt_hd(log, hd, "tx"); -} - -void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, - ...) { - va_list ap; - int n; - char buf[NGTCP2_LOG_BUFLEN]; - - if (!log->log_printf) { - return; - } - - va_start(ap, fmt); - n = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - if (n < 0 || (size_t)n >= sizeof(buf)) { - return; - } - - log->log_printf(log->user_data, (NGTCP2_LOG_HD " %s"), - timestamp_cast(log->last_ts - log->ts), log->scid, - strevent(ev), buf); -} - -void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { - ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT, - "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num, - strpkttype(hd), hd->type); -} diff --git a/deps/ngtcp2/lib/ngtcp2_log.h b/deps/ngtcp2/lib/ngtcp2_log.h deleted file mode 100644 index 985aa84b271..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_log.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_LOG_H -#define NGTCP2_LOG_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_pkt.h" - -struct ngtcp2_log { - /* log_printf is a sink to write log. NULL means no logging - output. */ - ngtcp2_printf log_printf; - /* ts is the time point used to write time delta in the log. */ - ngtcp2_tstamp ts; - /* last_ts is the most recent time point that this object is - told. */ - ngtcp2_tstamp last_ts; - /* user_data is user-defined opaque data which is passed to - log_pritnf. */ - void *user_data; - /* scid is SCID encoded as NULL-terminated hex string. */ - uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1]; -}; - -typedef struct ngtcp2_log ngtcp2_log; - -void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, - ngtcp2_printf log_printf, ngtcp2_tstamp ts, - void *user_data); - -void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_frame *fr); -void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const ngtcp2_frame *fr); - -void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, - const uint32_t *sv, size_t nsv); - -void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr); - -void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, - const ngtcp2_transport_params *params); - -void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, - uint8_t flags, ngtcp2_tstamp sent_ts); - -void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); - -void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); - -void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); - -#endif /* NGTCP2_LOG_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_macro.h b/deps/ngtcp2/lib/ngtcp2_macro.h deleted file mode 100644 index e2603aae15d..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_macro.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_MACRO_H -#define NGTCP2_MACRO_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <stddef.h> - -#include <ngtcp2/ngtcp2.h> - -#define ngtcp2_min(A, B) ((A) < (B) ? (A) : (B)) -#define ngtcp2_max(A, B) ((A) > (B) ? (A) : (B)) - -#define ngtcp2_struct_of(ptr, type, member) \ - ((type *)(void *)((char *)(ptr)-offsetof(type, member))) - -/* ngtcp2_list_insert inserts |T| before |*PD|. The contract is that - this is singly linked list, and the next element is pointed by next - field of the previous element. |PD| must be a pointer to the - pointer to the next field of the previous element of |*PD|: if C is - the previous element of |PD|, PD = &C->next. */ -#define ngtcp2_list_insert(T, PD) \ - do { \ - (T)->next = *(PD); \ - *(PD) = (T); \ - } while (0) - -#endif /* NGTCP2_MACRO_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/lib/ngtcp2_map.c deleted file mode 100644 index 12a20283b4c..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_map.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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 "ngtcp2_map.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_conv.h" - -#define INITIAL_TABLE_LENGTH 256 - -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { - map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; - map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket)); - if (map->table == NULL) { - return NGTCP2_ERR_NOMEM; - } - - map->size = 0; - - return 0; -} - -void ngtcp2_map_free(ngtcp2_map *map) { - size_t i; - ngtcp2_map_bucket *bkt; - - if (!map) { - return; - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } - } - - ngtcp2_mem_free(map->mem, map->table); -} - -void ngtcp2_map_each_free(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), - void *ptr) { - uint32_t i; - ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - - if (bkt->ptr) { - func(bkt->ptr, ptr); - bkt->ptr = NULL; - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); - continue; - } - - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - func(ngtcp2_ksl_it_get(&it), ptr); - } - - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } - } -} - -int ngtcp2_map_each(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), - void *ptr) { - int rv; - uint32_t i; - ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - - if (bkt->ptr) { - rv = func(bkt->ptr, ptr); - if (rv != 0) { - return rv; - } - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); - continue; - } - - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - rv = func(ngtcp2_ksl_it_get(&it), ptr); - if (rv != 0) { - return rv; - } - } - } - } - return 0; -} - -void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) { - entry->key = key; - entry->next = NULL; -} - -/* FNV1a hash */ -static uint32_t hash(key_type key, uint32_t mod) { - uint8_t *p, *end; - uint32_t h = 0x811C9DC5u; - - key = ngtcp2_htonl64(key); - p = (uint8_t *)&key; - end = p + sizeof(key_type); - - for (; p != end;) { - h ^= *p++; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } - - return h & (mod - 1); -} - -static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { - return *(key_type *)lhs < *(key_type *)rhs; -} - -static int map_insert(ngtcp2_map *map, ngtcp2_map_bucket *table, - uint32_t tablelen, ngtcp2_map_entry *entry) { - uint32_t h = hash(entry->key, tablelen); - ngtcp2_map_bucket *bkt = &table[h]; - const ngtcp2_mem *mem = map->mem; - int rv; - - if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) { - bkt->ptr = entry; - return 0; - } - - if (!bkt->ksl) { - bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl)); - if (bkt->ksl == NULL) { - return NGTCP2_ERR_NOMEM; - } - ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem); - } - - if (bkt->ptr) { - rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); - if (rv != 0) { - return rv; - } - - bkt->ptr = NULL; - } - - return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry); -} - -/* new_tablelen must be power of 2 */ -static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) { - uint32_t i; - ngtcp2_map_bucket *new_table; - ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; - int rv; - - new_table = - ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket)); - if (new_table == NULL) { - return NGTCP2_ERR_NOMEM; - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - - if (bkt->ptr) { - rv = map_insert(map, new_table, new_tablelen, bkt->ptr); - if (rv != 0) { - goto fail; - } - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); - continue; - } - - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - rv = map_insert(map, new_table, new_tablelen, ngtcp2_ksl_it_get(&it)); - if (rv != 0) { - goto fail; - } - } - } - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } - } - - ngtcp2_mem_free(map->mem, map->table); - map->tablelen = new_tablelen; - map->table = new_table; - - return 0; - -fail: - for (i = 0; i < new_tablelen; ++i) { - bkt = &new_table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } - } - - return rv; -} - -int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) { - int rv; - - /* Load factor is 0.75 */ - if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2); - if (rv != 0) { - return rv; - } - } - rv = map_insert(map, map->table, map->tablelen, new_entry); - if (rv != 0) { - return rv; - } - ++map->size; - return 0; -} - -ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key) { - ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - ngtcp2_ksl_it it; - - if (bkt->ptr) { - if (bkt->ptr->key == key) { - return bkt->ptr; - } - return NULL; - } - - if (bkt->ksl) { - it = ngtcp2_ksl_lower_bound(bkt->ksl, &key); - if (ngtcp2_ksl_it_end(&it) || *(key_type *)ngtcp2_ksl_it_key(&it) != key) { - return NULL; - } - return ngtcp2_ksl_it_get(&it); - } - - return NULL; -} - -int ngtcp2_map_remove(ngtcp2_map *map, key_type key) { - ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - int rv; - - if (bkt->ptr) { - if (bkt->ptr->key == key) { - bkt->ptr = NULL; - --map->size; - return 0; - } - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - if (bkt->ksl) { - rv = ngtcp2_ksl_remove(bkt->ksl, NULL, &key); - if (rv != 0) { - return rv; - } - --map->size; - return 0; - } - - return NGTCP2_ERR_INVALID_ARGUMENT; -} - -void ngtcp2_map_clear(ngtcp2_map *map) { - uint32_t i; - ngtcp2_map_bucket *bkt; - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - bkt->ptr = NULL; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } - } - - map->size = 0; -} - -size_t ngtcp2_map_size(ngtcp2_map *map) { return map->size; } diff --git a/deps/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/lib/ngtcp2_map.h deleted file mode 100644 index 20afce24e9a..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_map.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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. - */ -#ifndef NGTCP2_MAP_H -#define NGTCP2_MAP_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" -#include "ngtcp2_ksl.h" - -/* Implementation of unordered map */ - -typedef uint64_t key_type; - -typedef struct ngtcp2_map_entry { - struct ngtcp2_map_entry *next; - key_type key; -} ngtcp2_map_entry; - -typedef struct ngtcp2_map_bucket { - ngtcp2_map_entry *ptr; - ngtcp2_ksl *ksl; -} ngtcp2_map_bucket; - -typedef struct { - ngtcp2_map_bucket *table; - const ngtcp2_mem *mem; - size_t size; - uint32_t tablelen; -} ngtcp2_map; - -/* - * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); - -/* - * Deallocates any resources allocated for |map|. The stored entries - * are not freed by this function. Use ngtcp2_map_each_free() to free - * each entries. - */ -void ngtcp2_map_free(ngtcp2_map *map); - -/* - * Deallocates each entries using |func| function and any resources - * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as - * send argument. The return value of the |func| will be ignored. - */ -void ngtcp2_map_each_free(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), - void *ptr); - -/* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * The item associated by |key| already exists. - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry); - -/* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. - */ -ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key); - -/* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. - */ -int ngtcp2_map_remove(ngtcp2_map *map, key_type key); - -/* - * Removes all entries from |map|. - */ -void ngtcp2_map_clear(ngtcp2_map *map); - -/* - * Returns the number of items stored in the map |map|. - */ -size_t ngtcp2_map_size(ngtcp2_map *map); - -/* - * Applies the function |func| to each entry in the |map| with the - * optional user supplied pointer |ptr|. - * - * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the - * |func| for further entries and return the return value of the - * |func| immediately. Thus, this function returns 0 if all the - * invocations of the |func| return 0, or nonzero value which the last - * invocation of |func| returns. - * - * Don't use this function to free each entry. Use - * ngtcp2_map_each_free() instead. - */ -int ngtcp2_map_each(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr); - -#endif /* NGTCP2_MAP_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/lib/ngtcp2_mem.c deleted file mode 100644 index 2c036ad1634..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_mem.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2014 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 "ngtcp2_mem.h" - -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; - - return malloc(size); -} - -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; - - free(ptr); -} - -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; - - return calloc(nmemb, size); -} - -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; - - return realloc(ptr, size); -} - -static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free, - default_calloc, default_realloc}; - -const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; } - -void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); -} - -void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) { - free_func(ptr, mem_user_data); -} - -void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); -} - -void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); -} diff --git a/deps/ngtcp2/lib/ngtcp2_mem.h b/deps/ngtcp2/lib/ngtcp2_mem.h deleted file mode 100644 index cdecf8763a5..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_mem.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2014 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. - */ -#ifndef NGTCP2_MEM_H -#define NGTCP2_MEM_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -/* Convenient wrapper functions to call allocator function in - |mem|. */ -void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size); -void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr); -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data); -void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size); -void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size); - -#endif /* NGTCP2_MEM_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/lib/ngtcp2_path.c deleted file mode 100644 index 3f35f28ef6a..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_path.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_path.h" - -#include <string.h> - -#include "ngtcp2_addr.h" - -void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, - const ngtcp2_addr *remote) { - path->local = *local; - path->remote = *remote; -} - -void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) { - ngtcp2_addr_copy(&dest->local, &src->local); - ngtcp2_addr_copy(&dest->remote, &src->remote); -} - -int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { - return ngtcp2_addr_eq(&a->local, &b->local) && - ngtcp2_addr_eq(&a->remote, &b->remote); -} - -void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, void *local_user_data, - const struct sockaddr *remote_addr, - size_t remote_addrlen, void *remote_user_data) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, - 0, local_user_data); - ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0, - remote_user_data); - - ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen); - ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen); -} - -void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, - const ngtcp2_path *path) { - ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen, - path->local.user_data, path->remote.addr, - path->remote.addrlen, path->remote.user_data); -} - -void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, - 0, NULL); - ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0, NULL); -} diff --git a/deps/ngtcp2/lib/ngtcp2_path.h b/deps/ngtcp2/lib/ngtcp2_path.h deleted file mode 100644 index 1b2e2f87c8c..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_path.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_PATH_H -#define NGTCP2_PATH_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -/* - * ngtcp2_path_init initializes |path| with the given addresses. Note - * that the buffer pointed by local->addr and remote->addr are not - * copied. Their pointer values are assigned instead. - */ -void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, - const ngtcp2_addr *remote); - -/* - * ngtcp2_path_copy copies |src| into |dest|. This function assumes - * that |dest| has enough buffer to store the deep copy of src->local - * and src->remote. - */ -void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); - -/* - * ngtcp2_path_eq returns nonzero if |a| equals |b| such that - * ngtcp2_addr_eq(&a->local, &b->local) && ngtcp2_addr_eq(&a->remote, - * &b->remote) is true. - */ -int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); - -/* - * ngtcp2_path_storage_init2 initializes |ps| using |path| as initial - * data. - */ -void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, - const ngtcp2_path *path); - -#endif /* NGTCP2_PATH_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/lib/ngtcp2_pkt.c deleted file mode 100644 index 905af29a822..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_pkt.c +++ /dev/null @@ -1,2271 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_pkt.h" - -#include <assert.h> -#include <string.h> -#include <stdio.h> - -#include "ngtcp2_conv.h" -#include "ngtcp2_str.h" -#include "ngtcp2_macro.h" -#include "ngtcp2_cid.h" -#include "ngtcp2_mem.h" - -int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts, - const ngtcp2_mem *mem) { - *ppc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pkt_chain) + pktlen); - if (*ppc == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_path_storage_init2(&(*ppc)->path, path); - (*ppc)->next = NULL; - (*ppc)->pkt = (uint8_t *)(*ppc) + sizeof(ngtcp2_pkt_chain); - (*ppc)->pktlen = pktlen; - (*ppc)->ts = ts; - - memcpy((*ppc)->pkt, pkt, pktlen); - - return 0; -} - -void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, pc); -} - -int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, - size_t *pdcidlen, const uint8_t **pscid, - size_t *pscidlen, const uint8_t *data, - size_t datalen, size_t short_dcidlen) { - size_t len; - uint32_t version; - size_t dcidlen, scidlen; - - assert(datalen); - - if (data[0] & NGTCP2_HEADER_FORM_BIT) { - /* 1 byte (Header Form, Fixed Bit, Long Packet Type, Type-Specific bits) - * 4 bytes Version - * 1 byte DCID Length - * 1 byte SCID Length - */ - len = 1 + 4 + 1 + 1; - if (datalen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - dcidlen = data[5]; - len += dcidlen; - if (datalen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - scidlen = data[5 + 1 + dcidlen]; - len += scidlen; - if (datalen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - version = ngtcp2_get_uint32(&data[1]); - - if ((version == 0 || version == NGTCP2_PROTO_VER) && - (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - *pversion = version; - *pdcid = &data[6]; - *pdcidlen = dcidlen; - *pscid = &data[6 + dcidlen + 1]; - *pscidlen = scidlen; - - if (version && version != NGTCP2_PROTO_VER) { - return 1; - } - return 0; - } - - assert(short_dcidlen <= NGTCP2_MAX_CIDLEN); - - len = 1 + short_dcidlen; - if (datalen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - *pversion = NGTCP2_PROTO_VER; - *pdcid = &data[1]; - *pdcidlen = short_dcidlen; - *pscid = NULL; - *pscidlen = 0; - - return 0; -} - -void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, - const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - int64_t pkt_num, size_t pkt_numlen, uint32_t version, - size_t len) { - hd->flags = flags; - hd->type = type; - if (dcid) { - hd->dcid = *dcid; - } else { - ngtcp2_cid_zero(&hd->dcid); - } - if (scid) { - hd->scid = *scid; - } else { - ngtcp2_cid_zero(&hd->scid); - } - hd->pkt_num = pkt_num; - hd->token.base = NULL; - hd->token.len = 0; - hd->pkt_numlen = pkt_numlen; - hd->version = version; - hd->len = len; -} - -static int has_mask(uint8_t b, uint8_t mask) { return (b & mask) == mask; } - -ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, - size_t pktlen) { - uint8_t type; - uint32_t version; - size_t dcil, scil; - const uint8_t *p; - size_t len = 0; - size_t n; - size_t ntokenlen = 0; - const uint8_t *token = NULL; - size_t tokenlen = 0; - uint64_t vi; - - if (pktlen < 5) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - version = ngtcp2_get_uint32(&pkt[1]); - - if (version == 0) { - type = NGTCP2_PKT_VERSION_NEGOTIATION; - /* This must be Version Negotiation packet which lacks packet - number and payload length fields. */ - len = 5 + 2; - } else { - if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - type = ngtcp2_pkt_get_type_long(pkt[0]); - switch (type) { - case NGTCP2_PKT_INITIAL: - len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN - - 1; /* Cut packet number field */ - break; - case NGTCP2_PKT_RETRY: - /* Retry packet does not have packet number and length fields */ - len = 5 + 2; - break; - case NGTCP2_PKT_HANDSHAKE: - case NGTCP2_PKT_0RTT: - len = NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */ - break; - default: - /* Unreachable */ - assert(0); - } - } - - if (pktlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - p = &pkt[5]; - dcil = *p; - if (dcil > NGTCP2_MAX_CIDLEN) { - /* QUIC v1 implementation never expect to receive CID length more - than NGTCP2_MAX_CIDLEN. */ - return NGTCP2_ERR_INVALID_ARGUMENT; - } - len += dcil; - - if (pktlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - p += 1 + dcil; - scil = *p; - if (scil > NGTCP2_MAX_CIDLEN) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - len += scil; - - if (pktlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - p += 1 + scil; - - if (type == NGTCP2_PKT_INITIAL) { - /* Token Length */ - ntokenlen = ngtcp2_get_varint_len(p); - len += ntokenlen - 1; - - if (pktlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - vi = ngtcp2_get_varint(&ntokenlen, p); - if (pktlen - len < vi) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - tokenlen = (size_t)vi; - len += tokenlen; - - p += ntokenlen; - - if (tokenlen) { - token = p; - } - - p += tokenlen; - } - - switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: - case NGTCP2_PKT_RETRY: - break; - default: - /* Length */ - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (pktlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - } - - dest->flags = NGTCP2_PKT_FLAG_LONG_FORM; - dest->type = type; - dest->version = version; - dest->pkt_num = 0; - dest->pkt_numlen = 0; - - p = &pkt[6]; - ngtcp2_cid_init(&dest->dcid, p, dcil); - p += dcil + 1; - ngtcp2_cid_init(&dest->scid, p, scil); - p += scil; - - dest->token.base = (uint8_t *)token; - dest->token.len = tokenlen; - p += ntokenlen + tokenlen; - - switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: - case NGTCP2_PKT_RETRY: - dest->len = 0; - break; - default: - vi = ngtcp2_get_varint(&n, p); - if (vi > SIZE_MAX) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - dest->len = (size_t)vi; - p += n; - } - - assert((size_t)(p - pkt) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, - size_t pktlen, size_t dcidlen) { - size_t len = 1 + dcidlen; - const uint8_t *p = pkt; - - assert(dcidlen <= NGTCP2_MAX_CIDLEN); - - if (pktlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) || - (pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - p = &pkt[1]; - - dest->type = NGTCP2_PKT_SHORT; - - ngtcp2_cid_init(&dest->dcid, p, dcidlen); - p += dcidlen; - - /* Set 0 to SCID so that we don't accidentally reference it and gets - garbage. */ - ngtcp2_cid_zero(&dest->scid); - - dest->flags = NGTCP2_PKT_FLAG_NONE; - dest->version = 0; - dest->len = 0; - dest->pkt_num = 0; - dest->pkt_numlen = 0; - - assert((size_t)(p - pkt) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, - const ngtcp2_pkt_hd *hd) { - uint8_t *p; - size_t len = NGTCP2_MIN_LONG_HEADERLEN + hd->dcid.datalen + hd->scid.datalen - - 2; /* NGTCP2_MIN_LONG_HEADERLEN includes 1 byte for - len and 1 byte for packet number. */ - - if (hd->type != NGTCP2_PKT_RETRY) { - len += 2 /* Length */ + hd->pkt_numlen; - } - - if (hd->type == NGTCP2_PKT_INITIAL) { - len += ngtcp2_put_varint_len(hd->token.len) + hd->token.len; - } - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK | - (uint8_t)(hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1); - p = ngtcp2_put_uint32be(p, hd->version); - *p++ = (uint8_t)hd->dcid.datalen; - if (hd->dcid.datalen) { - p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen); - } - *p++ = (uint8_t)hd->scid.datalen; - if (hd->scid.datalen) { - p = ngtcp2_cpymem(p, hd->scid.data, hd->scid.datalen); - } - - if (hd->type == NGTCP2_PKT_INITIAL) { - p = ngtcp2_put_varint(p, hd->token.len); - if (hd->token.len) { - p = ngtcp2_cpymem(p, hd->token.base, hd->token.len); - } - } - - if (hd->type != NGTCP2_PKT_RETRY) { - p = ngtcp2_put_varint14(p, (uint16_t)hd->len); - p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); - } - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, - const ngtcp2_pkt_hd *hd) { - uint8_t *p; - size_t len = 1 + hd->dcid.datalen + hd->pkt_numlen; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1); - if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) { - *p |= NGTCP2_SHORT_KEY_PHASE_BIT; - } - - ++p; - - if (hd->dcid.datalen) { - p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen); - } - - p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, - size_t payloadlen) { - uint8_t type; - - if (payloadlen == 0) { - return 0; - } - - type = payload[0]; - - switch (type) { - case NGTCP2_FRAME_PADDING: - return (ngtcp2_ssize)ngtcp2_pkt_decode_padding_frame(&dest->padding, - payload, payloadlen); - case NGTCP2_FRAME_RESET_STREAM: - return ngtcp2_pkt_decode_reset_stream_frame(&dest->reset_stream, payload, - payloadlen); - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - return ngtcp2_pkt_decode_connection_close_frame(&dest->connection_close, - payload, payloadlen); - case NGTCP2_FRAME_MAX_DATA: - return ngtcp2_pkt_decode_max_data_frame(&dest->max_data, payload, - payloadlen); - case NGTCP2_FRAME_MAX_STREAM_DATA: - return ngtcp2_pkt_decode_max_stream_data_frame(&dest->max_stream_data, - payload, payloadlen); - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - case NGTCP2_FRAME_MAX_STREAMS_UNI: - return ngtcp2_pkt_decode_max_streams_frame(&dest->max_streams, payload, - payloadlen); - case NGTCP2_FRAME_PING: - return ngtcp2_pkt_decode_ping_frame(&dest->ping, payload, payloadlen); - case NGTCP2_FRAME_DATA_BLOCKED: - return ngtcp2_pkt_decode_data_blocked_frame(&dest->data_blocked, payload, - payloadlen); - case NGTCP2_FRAME_STREAM_DATA_BLOCKED: - return ngtcp2_pkt_decode_stream_data_blocked_frame( - &dest->stream_data_blocked, payload, payloadlen); - case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: - case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: - return ngtcp2_pkt_decode_streams_blocked_frame(&dest->streams_blocked, - payload, payloadlen); - case NGTCP2_FRAME_NEW_CONNECTION_ID: - return ngtcp2_pkt_decode_new_connection_id_frame(&dest->new_connection_id, - payload, payloadlen); - case NGTCP2_FRAME_STOP_SENDING: - return ngtcp2_pkt_decode_stop_sending_frame(&dest->stop_sending, payload, - payloadlen); - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - return ngtcp2_pkt_decode_ack_frame(&dest->ack, payload, payloadlen); - case NGTCP2_FRAME_PATH_CHALLENGE: - return ngtcp2_pkt_decode_path_challenge_frame(&dest->path_challenge, - payload, payloadlen); - case NGTCP2_FRAME_PATH_RESPONSE: - return ngtcp2_pkt_decode_path_response_frame(&dest->path_response, payload, - payloadlen); - case NGTCP2_FRAME_CRYPTO: - return ngtcp2_pkt_decode_crypto_frame(&dest->crypto, payload, payloadlen); - case NGTCP2_FRAME_NEW_TOKEN: - return ngtcp2_pkt_decode_new_token_frame(&dest->new_token, payload, - payloadlen); - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - return ngtcp2_pkt_decode_retire_connection_id_frame( - &dest->retire_connection_id, payload, payloadlen); - case NGTCP2_FRAME_HANDSHAKE_DONE: - return ngtcp2_pkt_decode_handshake_done_frame(&dest->handshake_done, - payload, payloadlen); - default: - if (has_mask(type, NGTCP2_FRAME_STREAM)) { - return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen); - } - return NGTCP2_ERR_FRAME_ENCODING; - } -} - -ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest, - const uint8_t *payload, - size_t payloadlen) { - uint8_t type; - size_t len = 1 + 1; - const uint8_t *p; - size_t datalen; - size_t ndatalen = 0; - size_t n; - uint64_t vi; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - type = payload[0]; - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - if (type & NGTCP2_STREAM_OFF_BIT) { - ++len; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - } - - if (type & NGTCP2_STREAM_LEN_BIT) { - ++len; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - ndatalen = ngtcp2_get_varint_len(p); - len += ndatalen - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - vi = ngtcp2_get_varint(&ndatalen, p); - if (payloadlen - len < vi) { - return NGTCP2_ERR_FRAME_ENCODING; - } - datalen = (size_t)vi; - len += datalen; - } else { - len = payloadlen; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_STREAM; - dest->flags = (uint8_t)(type & ~NGTCP2_FRAME_STREAM); - dest->fin = (type & NGTCP2_STREAM_FIN_BIT) != 0; - dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); - p += n; - - if (type & NGTCP2_STREAM_OFF_BIT) { - dest->offset = ngtcp2_get_varint(&n, p); - p += n; - } else { - dest->offset = 0; - } - - if (type & NGTCP2_STREAM_LEN_BIT) { - p += ndatalen; - } else { - datalen = payloadlen - (size_t)(p - payload); - } - - if (datalen) { - dest->data[0].len = datalen; - dest->data[0].base = (uint8_t *)p; - dest->datacnt = 1; - p += datalen; - } else { - dest->datacnt = 0; - } - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t num_blks, max_num_blks; - size_t nnum_blks; - size_t len = 1 + 1 + 1 + 1 + 1; - const uint8_t *p; - size_t i, j; - ngtcp2_ack_blk *blk; - size_t n; - uint8_t type; - uint64_t vi; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - type = payload[0]; - - p = payload + 1; - - /* Largest Acknowledged */ - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - /* ACK Delay */ - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - /* ACK Block Count */ - nnum_blks = ngtcp2_get_varint_len(p); - len += nnum_blks - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - vi = ngtcp2_get_varint(&nnum_blks, p); - if (vi > SIZE_MAX / (1 + 1) || payloadlen - len < vi * (1 + 1)) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - num_blks = (size_t)vi; - len += num_blks * (1 + 1); - - p += nnum_blks; - - /* First ACK Block */ - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - for (i = 0; i < num_blks; ++i) { - /* Gap, and Additional ACK Block */ - for (j = 0; j < 2; ++j) { - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - } - } - - if (type == NGTCP2_FRAME_ACK_ECN) { - len += 3; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - for (i = 0; i < 3; ++i) { - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - } - } - - /* TODO We might not decode all blocks. It could be very large. */ - max_num_blks = ngtcp2_min(NGTCP2_MAX_ACK_BLKS, num_blks); - - p = payload + 1; - - dest->type = type; - dest->largest_ack = (int64_t)ngtcp2_get_varint(&n, p); - p += n; - dest->ack_delay = ngtcp2_get_varint(&n, p); - /* This value will be assigned in the upper layer. */ - dest->ack_delay_unscaled = 0; - p += n; - dest->num_blks = max_num_blks; - p += nnum_blks; - dest->first_ack_blklen = ngtcp2_get_varint(&n, p); - p += n; - - for (i = 0; i < max_num_blks; ++i) { - blk = &dest->blks[i]; - blk->gap = ngtcp2_get_varint(&n, p); - p += n; - blk->blklen = ngtcp2_get_varint(&n, p); - p += n; - } - for (i = max_num_blks; i < num_blks; ++i) { - p += ngtcp2_get_varint_len(p); - p += ngtcp2_get_varint_len(p); - } - - if (type == NGTCP2_FRAME_ACK_ECN) { - /* Just parse ECN section for now */ - for (i = 0; i < 3; ++i) { - ngtcp2_get_varint(&n, p); - p += n; - } - } - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest, - const uint8_t *payload, - size_t payloadlen) { - const uint8_t *p, *ep; - - assert(payloadlen > 0); - - p = payload + 1; - ep = payload + payloadlen; - - for (; p != ep && *p == NGTCP2_FRAME_PADDING; ++p) - ; - - dest->type = NGTCP2_FRAME_PADDING; - dest->len = (size_t)(p - payload); - - return dest->len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1 + 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - p += n; - n = ngtcp2_get_varint_len(p); - len += n - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - p += n; - n = ngtcp2_get_varint_len(p); - len += n - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_RESET_STREAM; - dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); - p += n; - dest->app_error_code = ngtcp2_get_varint(&n, p); - p += n; - dest->final_size = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame( - ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen) { - size_t len = 1 + 1 + 1; - const uint8_t *p; - size_t reasonlen; - size_t nreasonlen; - size_t n; - uint8_t type; - uint64_t vi; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - type = payload[0]; - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { - ++len; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - } - - nreasonlen = ngtcp2_get_varint_len(p); - len += nreasonlen - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - vi = ngtcp2_get_varint(&nreasonlen, p); - if (payloadlen - len < vi) { - return NGTCP2_ERR_FRAME_ENCODING; - } - reasonlen = (size_t)vi; - len += reasonlen; - - p = payload + 1; - - dest->type = type; - dest->error_code = ngtcp2_get_varint(&n, p); - p += n; - if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { - dest->frame_type = ngtcp2_get_varint(&n, p); - p += n; - } else { - dest->frame_type = 0; - } - dest->reasonlen = reasonlen; - p += nreasonlen; - if (reasonlen == 0) { - dest->reason = NULL; - } else { - dest->reason = (uint8_t *)p; - p += reasonlen; - } - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - dest->type = NGTCP2_FRAME_MAX_DATA; - dest->max_data = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame( - ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen) { - size_t len = 1 + 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_MAX_STREAM_DATA; - dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); - p += n; - dest->max_stream_data = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - dest->type = payload[0]; - dest->max_streams = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest, - const uint8_t *payload, - size_t payloadlen) { - (void)payload; - (void)payloadlen; - - dest->type = NGTCP2_FRAME_PING; - return 1; -} - -ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - dest->type = NGTCP2_FRAME_DATA_BLOCKED; - dest->offset = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_STREAM_DATA_BLOCKED; - dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); - p += n; - dest->offset = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame( - ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen) { - size_t len = 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - dest->type = payload[0]; - dest->stream_limit = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame( - ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen) { - size_t len = 1 + 1 + 1 + 1 + 16; - const uint8_t *p; - size_t n; - size_t cil; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - cil = *p; - if (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - len += cil; - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_NEW_CONNECTION_ID; - dest->seq = ngtcp2_get_varint(&n, p); - p += n; - dest->retire_prior_to = ngtcp2_get_varint(&n, p); - p += n + 1; - ngtcp2_cid_init(&dest->cid, p, cil); - p += cil; - memcpy(dest->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN); - p += NGTCP2_STATELESS_RESET_TOKENLEN; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - p += n; - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_STOP_SENDING; - dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); - p += n; - dest->app_error_code = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 8; - const uint8_t *p; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_PATH_CHALLENGE; - ngtcp2_cpymem(dest->data, p, sizeof(dest->data)); - p += sizeof(dest->data); - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 8; - const uint8_t *p; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - dest->type = NGTCP2_FRAME_PATH_RESPONSE; - ngtcp2_cpymem(dest->data, p, sizeof(dest->data)); - p += sizeof(dest->data); - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1 + 1; - const uint8_t *p; - size_t datalen; - size_t ndatalen; - size_t n; - uint64_t vi; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p += n; - - ndatalen = ngtcp2_get_varint_len(p); - len += ndatalen - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - vi = ngtcp2_get_varint(&ndatalen, p); - if (payloadlen - len < vi) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - datalen = (size_t)vi; - len += datalen; - - p = payload + 1; - - dest->type = NGTCP2_FRAME_CRYPTO; - dest->offset = ngtcp2_get_varint(&n, p); - p += n; - dest->data[0].len = datalen; - p += ndatalen; - if (dest->data[0].len) { - dest->data[0].base = (uint8_t *)p; - p += dest->data[0].len; - dest->datacnt = 1; - } else { - dest->data[0].base = NULL; - dest->datacnt = 0; - } - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1; - const uint8_t *p; - size_t n; - size_t datalen; - uint64_t vi; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - vi = ngtcp2_get_varint(&n, p); - if (payloadlen - len < vi) { - return NGTCP2_ERR_FRAME_ENCODING; - } - datalen = (size_t)vi; - len += datalen; - - dest->type = NGTCP2_FRAME_NEW_TOKEN; - dest->token.len = datalen; - p += n; - dest->token.base = (uint8_t *)p; - p += dest->token.len; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, - const uint8_t *payload, - size_t payloadlen) { - size_t len = 1 + 1; - const uint8_t *p; - size_t n; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - p = payload + 1; - - n = ngtcp2_get_varint_len(p); - len += n - 1; - - if (payloadlen < len) { - return NGTCP2_ERR_FRAME_ENCODING; - } - - dest->type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; - dest->seq = ngtcp2_get_varint(&n, p); - p += n; - - assert((size_t)(p - payload) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, - const uint8_t *payload, - size_t payloadlen) { - (void)payload; - (void)payloadlen; - - dest->type = NGTCP2_FRAME_HANDSHAKE_DONE; - return 1; -} - -ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, - ngtcp2_frame *fr) { - switch (fr->type) { - case NGTCP2_FRAME_STREAM: - return ngtcp2_pkt_encode_stream_frame(out, outlen, &fr->stream); - case NGTCP2_FRAME_ACK: - return ngtcp2_pkt_encode_ack_frame(out, outlen, &fr->ack); - case NGTCP2_FRAME_PADDING: - return ngtcp2_pkt_encode_padding_frame(out, outlen, &fr->padding); - case NGTCP2_FRAME_RESET_STREAM: - return ngtcp2_pkt_encode_reset_stream_frame(out, outlen, &fr->reset_stream); - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - return ngtcp2_pkt_encode_connection_close_frame(out, outlen, - &fr->connection_close); - case NGTCP2_FRAME_MAX_DATA: - return ngtcp2_pkt_encode_max_data_frame(out, outlen, &fr->max_data); - case NGTCP2_FRAME_MAX_STREAM_DATA: - return ngtcp2_pkt_encode_max_stream_data_frame(out, outlen, - &fr->max_stream_data); - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - case NGTCP2_FRAME_MAX_STREAMS_UNI: - return ngtcp2_pkt_encode_max_streams_frame(out, outlen, &fr->max_streams); - case NGTCP2_FRAME_PING: - return ngtcp2_pkt_encode_ping_frame(out, outlen, &fr->ping); - case NGTCP2_FRAME_DATA_BLOCKED: - return ngtcp2_pkt_encode_data_blocked_frame(out, outlen, &fr->data_blocked); - case NGTCP2_FRAME_STREAM_DATA_BLOCKED: - return ngtcp2_pkt_encode_stream_data_blocked_frame( - out, outlen, &fr->stream_data_blocked); - case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: - case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: - return ngtcp2_pkt_encode_streams_blocked_frame(out, outlen, - &fr->streams_blocked); - case NGTCP2_FRAME_NEW_CONNECTION_ID: - return ngtcp2_pkt_encode_new_connection_id_frame(out, outlen, - &fr->new_connection_id); - case NGTCP2_FRAME_STOP_SENDING: - return ngtcp2_pkt_encode_stop_sending_frame(out, outlen, &fr->stop_sending); - case NGTCP2_FRAME_PATH_CHALLENGE: - return ngtcp2_pkt_encode_path_challenge_frame(out, outlen, - &fr->path_challenge); - case NGTCP2_FRAME_PATH_RESPONSE: - return ngtcp2_pkt_encode_path_response_frame(out, outlen, - &fr->path_response); - case NGTCP2_FRAME_CRYPTO: - return ngtcp2_pkt_encode_crypto_frame(out, outlen, &fr->crypto); - case NGTCP2_FRAME_NEW_TOKEN: - return ngtcp2_pkt_encode_new_token_frame(out, outlen, &fr->new_token); - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - return ngtcp2_pkt_encode_retire_connection_id_frame( - out, outlen, &fr->retire_connection_id); - case NGTCP2_FRAME_HANDSHAKE_DONE: - return ngtcp2_pkt_encode_handshake_done_frame(out, outlen, - &fr->handshake_done); - default: - return NGTCP2_ERR_INVALID_ARGUMENT; - } -} - -ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen, - ngtcp2_stream *fr) { - size_t len = 1; - uint8_t flags = NGTCP2_STREAM_LEN_BIT; - uint8_t *p; - size_t i; - size_t datalen = 0; - - if (fr->fin) { - flags |= NGTCP2_STREAM_FIN_BIT; - } - - if (fr->offset) { - flags |= NGTCP2_STREAM_OFF_BIT; - len += ngtcp2_put_varint_len(fr->offset); - } - - len += ngtcp2_put_varint_len((uint64_t)fr->stream_id); - - for (i = 0; i < fr->datacnt; ++i) { - datalen += fr->data[i].len; - } - - len += ngtcp2_put_varint_len(datalen); - len += datalen; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = flags | NGTCP2_FRAME_STREAM; - - fr->flags = flags; - - p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); - - if (fr->offset) { - p = ngtcp2_put_varint(p, fr->offset); - } - - p = ngtcp2_put_varint(p, datalen); - - for (i = 0; i < fr->datacnt; ++i) { - assert(fr->data[i].len); - assert(fr->data[i].base); - p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); - } - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen, - ngtcp2_ack *fr) { - size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->largest_ack) + - ngtcp2_put_varint_len(fr->ack_delay) + - ngtcp2_put_varint_len(fr->num_blks) + - ngtcp2_put_varint_len(fr->first_ack_blklen); - uint8_t *p; - size_t i; - const ngtcp2_ack_blk *blk; - - for (i = 0; i < fr->num_blks; ++i) { - blk = &fr->blks[i]; - len += ngtcp2_put_varint_len(blk->gap); - len += ngtcp2_put_varint_len(blk->blklen); - } - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_ACK; - p = ngtcp2_put_varint(p, (uint64_t)fr->largest_ack); - p = ngtcp2_put_varint(p, fr->ack_delay); - p = ngtcp2_put_varint(p, fr->num_blks); - p = ngtcp2_put_varint(p, fr->first_ack_blklen); - - for (i = 0; i < fr->num_blks; ++i) { - blk = &fr->blks[i]; - p = ngtcp2_put_varint(p, blk->gap); - p = ngtcp2_put_varint(p, blk->blklen); - } - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen, - const ngtcp2_padding *fr) { - if (outlen < fr->len) { - return NGTCP2_ERR_NOBUF; - } - - memset(out, 0, fr->len); - - return (ngtcp2_ssize)fr->len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen, - const ngtcp2_reset_stream *fr) { - size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + - ngtcp2_put_varint_len(fr->app_error_code) + - ngtcp2_put_varint_len(fr->final_size); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_RESET_STREAM; - p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); - p = ngtcp2_put_varint(p, fr->app_error_code); - p = ngtcp2_put_varint(p, fr->final_size); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen, - const ngtcp2_connection_close *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->error_code) + - (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE - ? ngtcp2_put_varint_len(fr->frame_type) - : 0) + - ngtcp2_put_varint_len(fr->reasonlen) + fr->reasonlen; - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = fr->type; - p = ngtcp2_put_varint(p, fr->error_code); - if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { - p = ngtcp2_put_varint(p, fr->frame_type); - } - p = ngtcp2_put_varint(p, fr->reasonlen); - if (fr->reasonlen) { - p = ngtcp2_cpymem(p, fr->reason, fr->reasonlen); - } - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen, - const ngtcp2_max_data *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->max_data); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_MAX_DATA; - p = ngtcp2_put_varint(p, fr->max_data); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen, - const ngtcp2_max_stream_data *fr) { - size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + - ngtcp2_put_varint_len(fr->max_stream_data); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_MAX_STREAM_DATA; - p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); - p = ngtcp2_put_varint(p, fr->max_stream_data); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen, - const ngtcp2_max_streams *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->max_streams); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = fr->type; - p = ngtcp2_put_varint(p, fr->max_streams); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen, - const ngtcp2_ping *fr) { - (void)fr; - - if (outlen < 1) { - return NGTCP2_ERR_NOBUF; - } - - *out++ = NGTCP2_FRAME_PING; - - return 1; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen, - const ngtcp2_data_blocked *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->offset); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_DATA_BLOCKED; - p = ngtcp2_put_varint(p, fr->offset); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame( - uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr) { - size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + - ngtcp2_put_varint_len(fr->offset); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_STREAM_DATA_BLOCKED; - p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); - p = ngtcp2_put_varint(p, fr->offset); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen, - const ngtcp2_streams_blocked *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->stream_limit); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = fr->type; - p = ngtcp2_put_varint(p, fr->stream_limit); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen, - const ngtcp2_new_connection_id *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->seq) + - ngtcp2_put_varint_len(fr->retire_prior_to) + 1 + - fr->cid.datalen + NGTCP2_STATELESS_RESET_TOKENLEN; - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_NEW_CONNECTION_ID; - p = ngtcp2_put_varint(p, fr->seq); - p = ngtcp2_put_varint(p, fr->retire_prior_to); - *p++ = (uint8_t)fr->cid.datalen; - p = ngtcp2_cpymem(p, fr->cid.data, fr->cid.datalen); - p = ngtcp2_cpymem(p, fr->stateless_reset_token, - NGTCP2_STATELESS_RESET_TOKENLEN); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen, - const ngtcp2_stop_sending *fr) { - size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + - ngtcp2_put_varint_len(fr->app_error_code); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_STOP_SENDING; - p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); - p = ngtcp2_put_varint(p, fr->app_error_code); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen, - const ngtcp2_path_challenge *fr) { - size_t len = 1 + 8; - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_PATH_CHALLENGE; - p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data)); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen, - const ngtcp2_path_response *fr) { - size_t len = 1 + 8; - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_PATH_RESPONSE; - p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data)); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen, - const ngtcp2_crypto *fr) { - size_t len = 1; - uint8_t *p; - size_t i; - size_t datalen = 0; - - len += ngtcp2_put_varint_len(fr->offset); - - for (i = 0; i < fr->datacnt; ++i) { - datalen += fr->data[i].len; - } - - len += ngtcp2_put_varint_len(datalen); - len += datalen; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_CRYPTO; - - p = ngtcp2_put_varint(p, fr->offset); - p = ngtcp2_put_varint(p, datalen); - - for (i = 0; i < fr->datacnt; ++i) { - assert(fr->data[i].base); - p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); - } - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen, - const ngtcp2_new_token *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->token.len) + fr->token.len; - uint8_t *p; - - assert(fr->token.len); - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_NEW_TOKEN; - - p = ngtcp2_put_varint(p, fr->token.len); - p = ngtcp2_cpymem(p, fr->token.base, fr->token.len); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( - uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr) { - size_t len = 1 + ngtcp2_put_varint_len(fr->seq); - uint8_t *p; - - if (outlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = out; - - *p++ = NGTCP2_FRAME_RETIRE_CONNECTION_ID; - - p = ngtcp2_put_varint(p, fr->seq); - - assert((size_t)(p - out) == len); - - return (ngtcp2_ssize)len; -} - -ngtcp2_ssize -ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, - const ngtcp2_handshake_done *fr) { - (void)fr; - - if (outlen < 1) { - return NGTCP2_ERR_NOBUF; - } - - *out++ = NGTCP2_FRAME_HANDSHAKE_DONE; - - return 1; -} - -ngtcp2_ssize ngtcp2_pkt_write_version_negotiation( - uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid, - size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv, - size_t nsv) { - size_t len = 1 + 4 + 1 + dcidlen + 1 + scidlen + nsv * 4; - uint8_t *p; - size_t i; - - assert(dcidlen < 256); - assert(scidlen < 256); - - if (destlen < len) { - return NGTCP2_ERR_NOBUF; - } - - p = dest; - - *p++ = 0x80 | unused_random; - p = ngtcp2_put_uint32be(p, 0); - *p++ = (uint8_t)dcidlen; - if (dcidlen) { - p = ngtcp2_cpymem(p, dcid, dcidlen); - } - *p++ = (uint8_t)scidlen; - if (scidlen) { - p = ngtcp2_cpymem(p, scid, scidlen); - } - - for (i = 0; i < nsv; ++i) { - p = ngtcp2_put_uint32be(p, sv[i]); - } - - assert((size_t)(p - dest) == len); - - return (ngtcp2_ssize)len; -} - -size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest, - const uint8_t *payload, - size_t payloadlen) { - const uint8_t *end = payload + payloadlen; - - assert((payloadlen % sizeof(uint32_t)) == 0); - - for (; payload != end; payload += sizeof(uint32_t)) { - *dest++ = ngtcp2_get_uint32(payload); - } - - return payloadlen / sizeof(uint32_t); -} - -int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, - const uint8_t *payload, - size_t payloadlen) { - const uint8_t *p = payload; - - if (payloadlen < - NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - sr->rand = p; - sr->randlen = payloadlen - NGTCP2_STATELESS_RESET_TOKENLEN; - p += sr->randlen; - memcpy(sr->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN); - - return 0; -} - -int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload, - size_t payloadlen) { - size_t len = /* token */ 1 + NGTCP2_RETRY_TAGLEN; - - if (payloadlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - dest->token.base = (uint8_t *)payload; - dest->token.len = (size_t)(payloadlen - NGTCP2_RETRY_TAGLEN); - ngtcp2_cpymem(dest->tag, payload + dest->token.len, NGTCP2_RETRY_TAGLEN); - - return 0; -} - -int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num, - size_t n) { - int64_t expected = max_pkt_num + 1; - int64_t win = (int64_t)1 << n; - int64_t hwin = win / 2; - int64_t mask = win - 1; - int64_t cand = (expected & ~mask) | pkt_num; - - if (cand <= expected - hwin) { - assert(cand <= (int64_t)NGTCP2_MAX_VARINT - win); - return cand + win; - } - if (cand > expected + hwin && cand >= win) { - return cand - win; - } - return cand; -} - -int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr) { - int64_t largest_ack = fr->largest_ack; - size_t i; - - if (largest_ack < (int64_t)fr->first_ack_blklen) { - return NGTCP2_ERR_ACK_FRAME; - } - - largest_ack -= (int64_t)fr->first_ack_blklen; - - for (i = 0; i < fr->num_blks; ++i) { - if (largest_ack < (int64_t)fr->blks[i].gap + 2) { - return NGTCP2_ERR_ACK_FRAME; - } - - largest_ack -= (int64_t)fr->blks[i].gap + 2; - - if (largest_ack < (int64_t)fr->blks[i].blklen) { - return NGTCP2_ERR_ACK_FRAME; - } - - largest_ack -= (int64_t)fr->blks[i].blklen; - } - - return 0; -} - -ngtcp2_ssize -ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen, - const uint8_t *stateless_reset_token, - const uint8_t *rand, size_t randlen) { - uint8_t *p; - - if (destlen < - NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) { - return NGTCP2_ERR_NOBUF; - } - - if (randlen < NGTCP2_MIN_STATELESS_RESET_RANDLEN) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - p = dest; - - randlen = ngtcp2_min(destlen - NGTCP2_STATELESS_RESET_TOKENLEN, randlen); - - p = ngtcp2_cpymem(p, rand, randlen); - p = ngtcp2_cpymem(p, stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN); - *dest = (uint8_t)((*dest & 0x7fu) | 0x40u); - - return p - dest; -} - -ngtcp2_ssize -ngtcp2_pkt_write_retry(uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_cid *odcid, - const uint8_t *token, size_t tokenlen, - ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_aead_ctx *aead_ctx) { - ngtcp2_pkt_hd hd; - uint8_t pseudo_retry[1500]; - ngtcp2_ssize pseudo_retrylen; - uint8_t tag[NGTCP2_RETRY_TAGLEN]; - int rv; - uint8_t *p; - size_t offset; - - assert(tokenlen > 0); - assert(!ngtcp2_cid_eq(scid, odcid)); - - /* Retry packet is sent at most once per one connection attempt. In - the first connection attempt, client has to send random DCID - which is at least 8 bytes long. */ - if (odcid->datalen < 8) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_RETRY, dcid, - scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, - NGTCP2_PROTO_VER, /* len = */ 0); - - pseudo_retrylen = - ngtcp2_pkt_encode_pseudo_retry(pseudo_retry, sizeof(pseudo_retry), &hd, - /* unused = */ 0, odcid, token, tokenlen); - if (pseudo_retrylen < 0) { - return pseudo_retrylen; - } - - /* OpenSSL does not like NULL plaintext. */ - rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, - (const uint8_t *)NGTCP2_RETRY_NONCE, - sizeof(NGTCP2_RETRY_NONCE) - 1, pseudo_retry, - (size_t)pseudo_retrylen); - if (rv != 0) { - return rv; - } - - offset = 1 + odcid->datalen; - if (destlen < (size_t)pseudo_retrylen + sizeof(tag) - offset) { - return NGTCP2_ERR_NOBUF; - } - - p = ngtcp2_cpymem(dest, pseudo_retry + offset, - (size_t)pseudo_retrylen - offset); - p = ngtcp2_cpymem(p, tag, sizeof(tag)); - - return p - dest; -} - -ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( - uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, - const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen) { - uint8_t *p = dest; - ngtcp2_ssize nwrite; - - if (destlen < 1 + odcid->datalen) { - return NGTCP2_ERR_NOBUF; - } - - *p++ = (uint8_t)odcid->datalen; - p = ngtcp2_cpymem(p, odcid->data, odcid->datalen); - destlen -= (size_t)(p - dest); - - nwrite = ngtcp2_pkt_encode_hd_long(p, destlen, hd); - if (nwrite < 0) { - return nwrite; - } - - if (destlen < (size_t)nwrite + tokenlen) { - return NGTCP2_ERR_NOBUF; - } - - *p &= 0xf0; - *p |= unused; - - p += nwrite; - - p = ngtcp2_cpymem(p, token, tokenlen); - - return p - dest; -} - -int ngtcp2_pkt_verify_retry_tag(const ngtcp2_pkt_retry *retry, - const uint8_t *pkt, size_t pktlen, - ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_aead_ctx *aead_ctx) { - uint8_t pseudo_retry[1500]; - size_t pseudo_retrylen; - uint8_t *p = pseudo_retry; - int rv; - uint8_t tag[NGTCP2_RETRY_TAGLEN]; - - assert(pktlen >= sizeof(retry->tag)); - - if (sizeof(pseudo_retry) < - 1 + retry->odcid.datalen + pktlen - sizeof(retry->tag)) { - return NGTCP2_ERR_PROTO; - } - - *p++ = (uint8_t)retry->odcid.datalen; - p = ngtcp2_cpymem(p, retry->odcid.data, retry->odcid.datalen); - p = ngtcp2_cpymem(p, pkt, pktlen - sizeof(retry->tag)); - - pseudo_retrylen = (size_t)(p - pseudo_retry); - - /* OpenSSL does not like NULL plaintext. */ - rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, - (const uint8_t *)NGTCP2_RETRY_NONCE, - sizeof(NGTCP2_RETRY_NONCE) - 1, pseudo_retry, pseudo_retrylen); - if (rv != 0) { - return rv; - } - - if (0 != memcmp(retry->tag, tag, sizeof(retry->tag))) { - return NGTCP2_ERR_PROTO; - } - - return 0; -} - -size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, - size_t len, size_t left) { - size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) + - (offset ? ngtcp2_put_varint_len(offset) : 0); - - if (left <= n) { - return (size_t)-1; - } - - left -= n; - - if (left > 8 + 1073741823 && len > 1073741823) { -#if SIZE_MAX > UINT32_MAX - len = ngtcp2_min(len, 4611686018427387903lu); -#endif /* SIZE_MAX > UINT32_MAX */ - return ngtcp2_min(len, left - 8); - } - - if (left > 4 + 16383 && len > 16383) { - len = ngtcp2_min(len, 1073741823); - return ngtcp2_min(len, left - 4); - } - - if (left > 2 + 63 && len > 63) { - len = ngtcp2_min(len, 16383); - return ngtcp2_min(len, left - 2); - } - - len = ngtcp2_min(len, 63); - return ngtcp2_min(len, left - 1); -} - -size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { - size_t n = 1 /* type */ + ngtcp2_put_varint_len(offset); - - /* CRYPTO frame must contain nonzero length data. Return -1 if - there is no space to write crypto data. */ - if (left <= n + 1) { - return (size_t)-1; - } - - left -= n; - - if (left > 8 + 1073741823 && len > 1073741823) { -#if SIZE_MAX > UINT32_MAX - len = ngtcp2_min(len, 4611686018427387903lu); -#endif /* SIZE_MAX > UINT32_MAX */ - return ngtcp2_min(len, left - 8); - } - - if (left > 4 + 16383 && len > 16383) { - len = ngtcp2_min(len, 1073741823); - return ngtcp2_min(len, left - 4); - } - - if (left > 2 + 63 && len > 63) { - len = ngtcp2_min(len, 16383); - return ngtcp2_min(len, left - 2); - } - - len = ngtcp2_min(len, 63); - return ngtcp2_min(len, left - 1); -} - -uint8_t ngtcp2_pkt_get_type_long(uint8_t c) { - return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); -} - -int ngtcp2_pkt_verify_reserved_bits(uint8_t c) { - if (c & NGTCP2_HEADER_FORM_BIT) { - return (c & NGTCP2_LONG_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO; - } - return (c & NGTCP2_SHORT_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO; -} diff --git a/deps/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/lib/ngtcp2_pkt.h deleted file mode 100644 index 7348cb9a9d0..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_pkt.h +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_PKT_H -#define NGTCP2_PKT_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -/* QUIC header macros */ -#define NGTCP2_HEADER_FORM_BIT 0x80 -#define NGTCP2_FIXED_BIT_MASK 0x40 -#define NGTCP2_PKT_NUMLEN_MASK 0x03 - -/* Long header specific macros */ -#define NGTCP2_LONG_TYPE_MASK 0x30 -#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0c - -/* Short header specific macros */ -#define NGTCP2_SHORT_SPIN_BIT_MASK 0x20 -#define NGTCP2_SHORT_RESERVED_BIT_MASK 0x18 -#define NGTCP2_SHORT_KEY_PHASE_BIT 0x04 - -/* NGTCP2_SR_TYPE is a Type field of Stateless Reset. */ -#define NGTCP2_SR_TYPE 0x1f - -/* NGTCP2_MIN_LONG_HEADERLEN is the minimum length of long header. - That is (1|1|TT|RR|PP)<1> + VERSION<4> + DCIL<1> + SCIL<1> + - LENGTH<1> + PKN<1> */ -#define NGTCP2_MIN_LONG_HEADERLEN (1 + 4 + 1 + 1 + 1 + 1) - -#define NGTCP2_STREAM_FIN_BIT 0x01 -#define NGTCP2_STREAM_LEN_BIT 0x02 -#define NGTCP2_STREAM_OFF_BIT 0x04 - -/* NGTCP2_STREAM_OVERHEAD is the maximum number of bytes required - other than payload for STREAM frame. That is from type field to - the beginning of the payload. */ -#define NGTCP2_STREAM_OVERHEAD (1 + 8 + 8 + 8) - -/* NGTCP2_CRYPTO_OVERHEAD is the maximum number of bytes required - other than payload for CRYPTO frame. That is from type field to - the beginning of the payload. */ -#define NGTCP2_CRYPTO_OVERHEAD (1 + 8 + 8) - -/* NGTCP2_MIN_FRAME_PAYLOADLEN is the minimum frame payload length. */ -#define NGTCP2_MIN_FRAME_PAYLOADLEN 16 - -/* NGTCP2_MAX_VARINT is the maximum value which can be encoded in - variable-length integer encoding */ -#define NGTCP2_MAX_VARINT ((1ULL << 62) - 1) - -/* NGTCP2_MAX_SERVER_STREAM_ID_BIDI is the maximum bidirectional - server stream ID. */ -#define NGTCP2_MAX_SERVER_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffdll) -/* NGTCP2_MAX_CLIENT_STREAM_ID_BIDI is the maximum bidirectional - client stream ID. */ -#define NGTCP2_MAX_CLIENT_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffcll) -/* NGTCP2_MAX_SERVER_STREAM_ID_UNI is the maximum unidirectional - server stream ID. */ -#define NGTCP2_MAX_SERVER_STREAM_ID_UNI ((int64_t)0x3fffffffffffffffll) -/* NGTCP2_MAX_CLIENT_STREAM_ID_UNI is the maximum unidirectional - client stream ID. */ -#define NGTCP2_MAX_CLIENT_STREAM_ID_UNI ((int64_t)0x3ffffffffffffffell) - -/* NGTCP2_MAX_NUM_ACK_BLK is the maximum number of Additional ACK - blocks which this library can create, or decode. */ -#define NGTCP2_MAX_ACK_BLKS 32 - -/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */ -#define NGTCP2_MAX_PKT_NUM ((int64_t)((1ll << 62) - 1)) - -/* NGTCP2_MIN_PKT_EXPANDLEN is the minimum packet size expansion in - addition to the minimum DCID length to hide/trigger Stateless - Reset. */ -#define NGTCP2_MIN_PKT_EXPANDLEN 22 - -/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ -#define NGTCP2_RETRY_TAGLEN 16 - -typedef struct ngtcp2_pkt_retry { - ngtcp2_cid odcid; - ngtcp2_vec token; - uint8_t tag[NGTCP2_RETRY_TAGLEN]; -} ngtcp2_pkt_retry; - -typedef enum { - NGTCP2_FRAME_PADDING = 0x00, - NGTCP2_FRAME_PING = 0x01, - NGTCP2_FRAME_ACK = 0x02, - NGTCP2_FRAME_ACK_ECN = 0x03, - NGTCP2_FRAME_RESET_STREAM = 0x04, - NGTCP2_FRAME_STOP_SENDING = 0x05, - NGTCP2_FRAME_CRYPTO = 0x06, - NGTCP2_FRAME_NEW_TOKEN = 0x07, - NGTCP2_FRAME_STREAM = 0x08, - NGTCP2_FRAME_MAX_DATA = 0x10, - NGTCP2_FRAME_MAX_STREAM_DATA = 0x11, - NGTCP2_FRAME_MAX_STREAMS_BIDI = 0x12, - NGTCP2_FRAME_MAX_STREAMS_UNI = 0x13, - NGTCP2_FRAME_DATA_BLOCKED = 0x14, - NGTCP2_FRAME_STREAM_DATA_BLOCKED = 0x15, - NGTCP2_FRAME_STREAMS_BLOCKED_BIDI = 0x16, - NGTCP2_FRAME_STREAMS_BLOCKED_UNI = 0x17, - NGTCP2_FRAME_NEW_CONNECTION_ID = 0x18, - NGTCP2_FRAME_RETIRE_CONNECTION_ID = 0x19, - NGTCP2_FRAME_PATH_CHALLENGE = 0x1a, - NGTCP2_FRAME_PATH_RESPONSE = 0x1b, - NGTCP2_FRAME_CONNECTION_CLOSE = 0x1c, - NGTCP2_FRAME_CONNECTION_CLOSE_APP = 0x1d, - NGTCP2_FRAME_HANDSHAKE_DONE = 0x1e, -} ngtcp2_frame_type; - -typedef struct { - uint8_t type; - /** - * flags of decoded STREAM frame. This gets ignored when encoding - * STREAM frame. - */ - uint8_t flags; - uint8_t fin; - int64_t stream_id; - uint64_t offset; - /* datacnt is the number of elements that data contains. Although - the length of data is 1 in this definition, the library may - allocate extra bytes to hold more elements. */ - size_t datacnt; - /* data is the array of ngtcp2_vec which references data. */ - ngtcp2_vec data[1]; -} ngtcp2_stream; - -typedef struct { - uint64_t gap; - uint64_t blklen; -} ngtcp2_ack_blk; - -typedef struct { - uint8_t type; - int64_t largest_ack; - uint64_t ack_delay; - /** - * ack_delay_unscaled is an ack_delay multiplied by - * 2**ack_delay_component * NGTCP2_MICROSECONDS. - */ - ngtcp2_duration ack_delay_unscaled; - uint64_t first_ack_blklen; - size_t num_blks; - ngtcp2_ack_blk blks[1]; -} ngtcp2_ack; - -typedef struct { - uint8_t type; - /** - * The length of contiguous PADDING frames. - */ - size_t len; -} ngtcp2_padding; - -typedef struct { - uint8_t type; - int64_t stream_id; - uint64_t app_error_code; - uint64_t final_size; -} ngtcp2_reset_stream; - -typedef struct { - uint8_t type; - uint64_t error_code; - uint64_t frame_type; - size_t reasonlen; - uint8_t *reason; -} ngtcp2_connection_close; - -typedef struct { - uint8_t type; - /** - * max_data is Maximum Data. - */ - uint64_t max_data; -} ngtcp2_max_data; - -typedef struct { - uint8_t type; - int64_t stream_id; - uint64_t max_stream_data; -} ngtcp2_max_stream_data; - -typedef struct { - uint8_t type; - uint64_t max_streams; -} ngtcp2_max_streams; - -typedef struct { - uint8_t type; -} ngtcp2_ping; - -typedef struct { - uint8_t type; - uint64_t offset; -} ngtcp2_data_blocked; - -typedef struct { - uint8_t type; - int64_t stream_id; - uint64_t offset; -} ngtcp2_stream_data_blocked; - -typedef struct { - uint8_t type; - uint64_t stream_limit; -} ngtcp2_streams_blocked; - -typedef struct { - uint8_t type; - uint64_t seq; - uint64_t retire_prior_to; - ngtcp2_cid cid; - uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; -} ngtcp2_new_connection_id; - -typedef struct { - uint8_t type; - int64_t stream_id; - uint64_t app_error_code; -} ngtcp2_stop_sending; - -typedef struct { - uint8_t type; - uint8_t data[8]; -} ngtcp2_path_challenge; - -typedef struct { - uint8_t type; - uint8_t data[8]; -} ngtcp2_path_response; - -typedef struct { - uint8_t type; - uint64_t offset; - /* datacnt is the number of elements that data contains. Although - the length of data is 1 in this definition, the library may - allocate extra bytes to hold more elements. */ - size_t datacnt; - /* data is the array of ngtcp2_vec which references data. */ - ngtcp2_vec data[1]; -} ngtcp2_crypto; - -typedef struct { - uint8_t type; - ngtcp2_vec token; -} ngtcp2_new_token; - -typedef struct { - uint8_t type; - uint64_t seq; -} ngtcp2_retire_connection_id; - -typedef struct { - uint8_t type; -} ngtcp2_handshake_done; - -typedef union { - uint8_t type; - ngtcp2_stream stream; - ngtcp2_ack ack; - ngtcp2_padding padding; - ngtcp2_reset_stream reset_stream; - ngtcp2_connection_close connection_close; - ngtcp2_max_data max_data; - ngtcp2_max_stream_data max_stream_data; - ngtcp2_max_streams max_streams; - ngtcp2_ping ping; - ngtcp2_data_blocked data_blocked; - ngtcp2_stream_data_blocked stream_data_blocked; - ngtcp2_streams_blocked streams_blocked; - ngtcp2_new_connection_id new_connection_id; - ngtcp2_stop_sending stop_sending; - ngtcp2_path_challenge path_challenge; - ngtcp2_path_response path_response; - ngtcp2_crypto crypto; - ngtcp2_new_token new_token; - ngtcp2_retire_connection_id retire_connection_id; - ngtcp2_handshake_done handshake_done; -} ngtcp2_frame; - -struct ngtcp2_pkt_chain; -typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain; - -/* - * ngtcp2_pkt_chain is the chain of incoming packets buffered. - */ -struct ngtcp2_pkt_chain { - ngtcp2_path_storage path; - ngtcp2_pkt_chain *next; - uint8_t *pkt; - size_t pktlen; - ngtcp2_tstamp ts; -}; - -/* - * ngtcp2_pkt_chain_new allocates ngtcp2_pkt_chain objects, and - * assigns its pointer to |*ppc|. The content of buffer pointed by - * |pkt| of length |pktlen| is copied into |*ppc|. The packet is - * obtained via the network |path|. The values of path->local and - * path->remote are copied into |*ppc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, - const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts, - const ngtcp2_mem *mem); - -/* - * ngtcp2_pkt_chain_del deallocates |pc|. It also frees the memory - * pointed by |pc|. - */ -void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem); - -/* - * ngtcp2_pkt_hd_init initializes |hd| with the given values. If - * |dcid| and/or |scid| is NULL, DCID and SCID of |hd| is empty - * respectively. |pkt_numlen| is the number of bytes used to encode - * |pkt_num| and either 1, 2, or 4. |version| is QUIC version for - * long header. |len| is the length field of Initial, 0RTT, and - * Handshake packets. - */ -void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, - const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - int64_t pkt_num, size_t pkt_numlen, uint32_t version, - size_t len); - -/* - * ngtcp2_pkt_encode_hd_long encodes |hd| as QUIC long header into - * |out| which has length |outlen|. It returns the number of bytes - * written into |outlen| if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer is too short - */ -ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, - const ngtcp2_pkt_hd *hd); - -/* - * ngtcp2_pkt_encode_hd_short encodes |hd| as QUIC short header into - * |out| which has length |outlen|. It returns the number of bytes - * written into |outlen| if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer is too short - */ -ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, - const ngtcp2_pkt_hd *hd); - -/** - * @function - * - * `ngtcp2_pkt_decode_frame` decodes a QUIC frame from the buffer - * pointed by |payload| whose length is |payloadlen|. - * - * This function returns the number of bytes read to decode a single - * frame if it succeeds, or one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_FRAME_ENCODING` - * Frame is badly formatted; or frame type is unknown. - */ -ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, - size_t payloadlen); - -/** - * @function - * - * `ngtcp2_pkt_encode_frame` encodes a frame |fm| into the buffer - * pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written to the buffer, or - * one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, - ngtcp2_frame *fr); - -/* - * ngtcp2_pkt_decode_version_negotiation decodes Version Negotiation - * packet payload |payload| of length |payloadlen|, and stores the - * result in |dest|. |dest| must have enough capacity to store the - * result. |payloadlen| also must be a multiple of sizeof(uint32_t). - * - * This function returns the number of versions written in |dest|. - */ -size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_stateless_reset decodes Stateless Reset payload - * |payload| of length |payloadlen|. The |payload| must start with - * Stateless Reset Token. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Payloadlen is too short. - */ -int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_retry decodes Retry packet payload |payload| of - * length |payloadlen|. The |payload| must start with Retry token - * field. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Payloadlen is too short. - */ -int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_stream_frame decodes STREAM frame from |payload| - * of length |payloadlen|. The result is stored in the object pointed - * by |dest|. STREAM frame must start at payload[0]. This function - * finishes when it decodes one STREAM frame, and returns the exact - * number of bytes read to decode a frame if it succeeds, or one of - * the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include STREAM frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_ack_frame decodes ACK frame from |payload| of - * length |payloadlen|. The result is stored in the object pointed by - * |dest|. ACK frame must start at payload[0]. This function - * finishes when it decodes one ACK frame, and returns the exact - * number of bytes read to decode a frame if it succeeds, or one of - * the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include ACK frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_padding_frame decodes contiguous PADDING frames - * from |payload| of length |payloadlen|. It continues to parse - * frames as long as the frame type is PADDING. This finishes when it - * encounters the frame type which is not PADDING, or all input data - * is read. The first byte (payload[0]) must be NGTCP2_FRAME_PADDING. - * This function returns the exact number of bytes read to decode - * PADDING frames. - */ -size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_reset_stream_frame decodes RESET_STREAM frame - * from |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. RESET_STREAM frame must start at - * payload[0]. This function finishes when it decodes one - * RESET_STREAM frame, and returns the exact number of bytes read to - * decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include RESET_STREAM frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_connection_close_frame decodes CONNECTION_CLOSE - * frame from |payload| of length |payloadlen|. The result is stored - * in the object pointed by |dest|. CONNECTION_CLOSE frame must start - * at payload[0]. This function finishes it decodes one - * CONNECTION_CLOSE frame, and returns the exact number of bytes read - * to decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include CONNECTION_CLOSE frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame( - ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen); - -/* - * ngtcp2_pkt_decode_max_data_frame decodes MAX_DATA frame from - * |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. MAX_DATA frame must start at payload[0]. - * This function finishes when it decodes one MAX_DATA frame, and - * returns the exact number of bytes read to decode a frame if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include MAX_DATA frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_max_stream_data_frame decodes MAX_STREAM_DATA - * frame from |payload| of length |payloadlen|. The result is stored - * in the object pointed by |dest|. MAX_STREAM_DATA frame must start - * at payload[0]. This function finishes when it decodes one - * MAX_STREAM_DATA frame, and returns the exact number of bytes read - * to decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include MAX_STREAM_DATA frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame( - ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen); - -/* - * ngtcp2_pkt_decode_max_streams_frame decodes MAX_STREAMS frame from - * |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. MAX_STREAMS frame must start at - * payload[0]. This function finishes when it decodes one MAX_STREAMS - * frame, and returns the exact number of bytes read to decode a frame - * if it succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include MAX_STREAMS frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_ping_frame decodes PING frame from |payload| of - * length |payloadlen|. The result is stored in the object pointed by - * |dest|. PING frame must start at payload[0]. This function - * finishes when it decodes one PING frame, and returns the exact - * number of bytes read to decode a frame if it succeeds, or one of - * the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include PING frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_data_blocked_frame decodes DATA_BLOCKED frame - * from |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. DATA_BLOCKED frame must start at - * payload[0]. This function finishes when it decodes one - * DATA_BLOCKED frame, and returns the exact number of bytes read to - * decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include DATA_BLOCKED frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_stream_data_blocked_frame decodes - * STREAM_DATA_BLOCKED frame from |payload| of length |payloadlen|. - * The result is stored in the object pointed by |dest|. - * STREAM_DATA_BLOCKED frame must start at payload[0]. This function - * finishes when it decodes one STREAM_DATA_BLOCKED frame, and returns - * the exact number of bytes read to decode a frame if it succeeds, or - * one of the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include STREAM_DATA_BLOCKED frame. - */ -ngtcp2_ssize -ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_streams_blocked_frame decodes STREAMS_BLOCKED - * frame from |payload| of length |payloadlen|. The result is stored - * in the object pointed by |dest|. STREAMS_BLOCKED frame must start - * at payload[0]. This function finishes when it decodes one - * STREAMS_BLOCKED frame, and returns the exact number of bytes read - * to decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include STREAMS_BLOCKED frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame( - ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen); - -/* - * ngtcp2_pkt_decode_new_connection_id_frame decodes NEW_CONNECTION_ID - * frame from |payload| of length |payloadlen|. The result is stored - * in the object pointed by |dest|. NEW_CONNECTION_ID frame must - * start at payload[0]. This function finishes when it decodes one - * NEW_CONNECTION_ID frame, and returns the exact number of bytes read - * to decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include NEW_CONNECTION_ID frame; or the - * length of CID is strictly less than NGTCP2_MIN_CIDLEN or - * greater than NGTCP2_MAX_CIDLEN. - */ -ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame( - ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen); - -/* - * ngtcp2_pkt_decode_stop_sending_frame decodes STOP_SENDING frame - * from |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. STOP_SENDING frame must start at - * payload[0]. This function finishes when it decodes one - * STOP_SENDING frame, and returns the exact number of bytes read to - * decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include STOP_SENDING frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_path_challenge_frame decodes PATH_CHALLENGE frame - * from |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. PATH_CHALLENGE frame must start at - * payload[0]. This function finishes when it decodes one - * PATH_CHALLENGE frame, and returns the exact number of bytes read to - * decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include PATH_CHALLENGE frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_path_response_frame decodes PATH_RESPONSE frame - * from |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. PATH_RESPONSE frame must start at - * payload[0]. This function finishes when it decodes one - * PATH_RESPONSE frame, and returns the exact number of bytes read to - * decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include PATH_RESPONSE frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_crypto_frame decodes CRYPTO frame from |payload| - * of length |payloadlen|. The result is stored in the object pointed - * by |dest|. CRYPTO frame must start at payload[0]. This function - * finishes when it decodes one CRYPTO frame, and returns the exact - * number of bytes read to decode a frame if it succeeds, or one of - * the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include CRYPTO frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_new_token_frame decodes NEW_TOKEN frame from - * |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. NEW_TOKEN frame must start at - * payload[0]. This function finishes when it decodes one NEW_TOKEN - * frame, and returns the exact number of bytes read to decode a frame - * if it succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include NEW_TOKEN frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_retire_connection_id_frame decodes RETIRE_CONNECTION_ID - * frame from |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. RETIRE_CONNECTION_ID frame must start at - * payload[0]. This function finishes when it decodes one RETIRE_CONNECTION_ID - * frame, and returns the exact number of bytes read to decode a frame - * if it succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include RETIRE_CONNECTION_ID frame. - */ -ngtcp2_ssize -ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_decode_handshake_done_frame decodes HANDSHAKE_DONE frame - * from |payload| of length |payloadlen|. The result is stored in the - * object pointed by |dest|. HANDSHAKE_DONE frame must start at - * payload[0]. This function finishes when it decodes one - * HANDSHAKE_DONE frame, and returns the exact number of bytes read to - * decode a frame if it succeeds, or one of the following negative - * error codes: - * - * NGTCP2_ERR_FRAME_ENCODING - * Payload is too short to include HANDSHAKE_DONE frame. - */ -ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, - const uint8_t *payload, - size_t payloadlen); - -/* - * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the - * buffer pointed by |out| of length |outlen|. - * - * This function assigns <the serialized frame type> & - * ~NGTCP2_FRAME_STREAM to fr->flags. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen, - ngtcp2_stream *fr); - -/* - * ngtcp2_pkt_encode_ack_frame encodes ACK frame |fr| into the buffer - * pointed by |out| of length |outlen|. - * - * This function assigns <the serialized frame type> & - * ~NGTCP2_FRAME_ACK to fr->flags. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen, - ngtcp2_ack *fr); - -/* - * ngtcp2_pkt_encode_padding_frame encodes PADDING frame |fr| into the - * buffer pointed by |out| of length |outlen|. - * - * This function encodes consecutive fr->len PADDING frames. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write frame(s). - */ -ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen, - const ngtcp2_padding *fr); - -/* - * ngtcp2_pkt_encode_reset_stream_frame encodes RESET_STREAM frame - * |fr| into the buffer pointed by |out| of length |buflen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen, - const ngtcp2_reset_stream *fr); - -/* - * ngtcp2_pkt_encode_connection_close_frame encodes CONNECTION_CLOSE - * frame |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen, - const ngtcp2_connection_close *fr); - -/* - * ngtcp2_pkt_encode_max_data_frame encodes MAX_DATA frame |fr| into - * the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen, - const ngtcp2_max_data *fr); - -/* - * ngtcp2_pkt_encode_max_stream_data_frame encodes MAX_STREAM_DATA - * frame |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen, - const ngtcp2_max_stream_data *fr); - -/* - * ngtcp2_pkt_encode_max_streams_frame encodes MAX_STREAMS - * frame |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen, - const ngtcp2_max_streams *fr); - -/* - * ngtcp2_pkt_encode_ping_frame encodes PING frame |fr| into the - * buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen, - const ngtcp2_ping *fr); - -/* - * ngtcp2_pkt_encode_data_blocked_frame encodes DATA_BLOCKED frame - * |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen, - const ngtcp2_data_blocked *fr); - -/* - * ngtcp2_pkt_encode_stream_data_blocked_frame encodes - * STREAM_DATA_BLOCKED frame |fr| into the buffer pointed by |out| of - * length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame( - uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr); - -/* - * ngtcp2_pkt_encode_streams_blocked_frame encodes STREAMS_BLOCKED - * frame |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen, - const ngtcp2_streams_blocked *fr); - -/* - * ngtcp2_pkt_encode_new_connection_id_frame encodes NEW_CONNECTION_ID - * frame |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen, - const ngtcp2_new_connection_id *fr); - -/* - * ngtcp2_pkt_encode_stop_sending_frame encodes STOP_SENDING frame - * |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen, - const ngtcp2_stop_sending *fr); - -/* - * ngtcp2_pkt_encode_path_challenge_frame encodes PATH_CHALLENGE frame - * |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen, - const ngtcp2_path_challenge *fr); - -/* - * ngtcp2_pkt_encode_path_response_frame encodes PATH_RESPONSE frame - * |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen, - const ngtcp2_path_response *fr); - -/* - * ngtcp2_pkt_encode_crypto_frame encodes CRYPTO frame |fr| into the - * buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen, - const ngtcp2_crypto *fr); - -/* - * ngtcp2_pkt_encode_new_token_frame encodes NEW_TOKEN frame |fr| into - * the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen, - const ngtcp2_new_token *fr); - -/* - * ngtcp2_pkt_encode_retire_connection_id_frame encodes RETIRE_CONNECTION_ID - * frame |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( - uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr); - -/* - * ngtcp2_pkt_encode_handshake_done_frame encodes HANDSHAKE_DONE frame - * |fr| into the buffer pointed by |out| of length |outlen|. - * - * This function returns the number of bytes written if it succeeds, - * or one of the following negative error codes: - * - * NGTCP2_ERR_NOBUF - * Buffer does not have enough capacity to write a frame. - */ -ngtcp2_ssize -ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, - const ngtcp2_handshake_done *fr); - -/* - * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for - * |pkt_num|, which is expected to be least significant |n| bits. The - * |max_pkt_num| is the highest successfully authenticated packet - * number. - */ -int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num, - size_t n); - -/* - * ngtcp2_pkt_validate_ack checks that ack is malformed or not. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_ACK_FRAME - * ACK frame is malformed - */ -int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr); - -/* - * ngtcp2_pkt_stream_max_datalen returns the maximum number of bytes - * which can be sent for stream denoted by |stream_id|. |offset| is - * an offset of within the stream. |len| is the estimated number of - * bytes to be sent. |left| is the size of buffer. If |left| is too - * small to write STREAM frame, this function returns (size_t)-1. - */ -size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, - size_t len, size_t left); - -/* - * ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes - * which can be sent for crypto stream. |offset| is an offset of - * within the crypto stream. |len| is the estimated number of bytes - * to be sent. |left| is the size of buffer. If |left| is too small - * to write CRYPTO frame, this function returns (size_t)-1. - */ -size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left); - -/* - * ngtcp2_pkt_verify_reserved_bits verifies that the first byte |c| of - * the packet header has the correct reserved bits. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_PROTO - * Reserved bits has wrong value. - */ -int ngtcp2_pkt_verify_reserved_bits(uint8_t c); - -/* - * ngtcp2_pkt_encode_pseudo_retry encodes Retry pseudo-packet in the - * buffer pointed by |dest| of length |destlen|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_BUF - * Buffer is too short. - */ -ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( - uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, - const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen); - -/* - * ngtcp2_pkt_verify_retry_tag verifies Retry packet. The buffer - * pointed by |pkt| of length |pktlen| must contain Retry packet - * including packet header. The odcid and tag fields of |retry| must - * be specified. |aead| must be AEAD_AES_128_GCM. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_PROTO - * Verification failed. - */ -int ngtcp2_pkt_verify_retry_tag(const ngtcp2_pkt_retry *retry, - const uint8_t *pkt, size_t pktlen, - ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_aead_ctx *aead_ctx); - -#endif /* NGTCP2_PKT_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/lib/ngtcp2_ppe.c deleted file mode 100644 index e2c47fd0c5f..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_ppe.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_ppe.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_str.h" -#include "ngtcp2_conv.h" - -void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, - ngtcp2_crypto_cc *cc) { - ngtcp2_buf_init(&ppe->buf, out, outlen); - - ppe->hdlen = 0; - ppe->len_offset = 0; - ppe->pkt_num_offset = 0; - ppe->pkt_numlen = 0; - ppe->pkt_num = 0; - ppe->sample_offset = 0; - ppe->cc = cc; -} - -int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { - ngtcp2_ssize rv; - ngtcp2_buf *buf = &ppe->buf; - ngtcp2_crypto_cc *cc = ppe->cc; - - if (ngtcp2_buf_left(buf) < cc->aead_overhead) { - return NGTCP2_ERR_NOBUF; - } - - if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { - ppe->len_offset = 1 + 4 + 1 + hd->dcid.datalen + 1 + hd->scid.datalen; - if (hd->type == NGTCP2_PKT_INITIAL) { - ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len; - } - ppe->pkt_num_offset = ppe->len_offset + 2; - rv = ngtcp2_pkt_encode_hd_long( - buf->last, ngtcp2_buf_left(buf) - cc->aead_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_overhead, hd); - } - if (rv < 0) { - return (int)rv; - } - - ppe->sample_offset = ppe->pkt_num_offset + 4; - - buf->last += rv; - - ppe->pkt_numlen = hd->pkt_numlen; - ppe->hdlen = (size_t)rv; - - ppe->pkt_num = hd->pkt_num; - - return 0; -} - -int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) { - ngtcp2_ssize rv; - ngtcp2_buf *buf = &ppe->buf; - ngtcp2_crypto_cc *cc = ppe->cc; - - if (ngtcp2_buf_left(buf) < cc->aead_overhead) { - return NGTCP2_ERR_NOBUF; - } - - rv = ngtcp2_pkt_encode_frame(buf->last, - ngtcp2_buf_left(buf) - cc->aead_overhead, fr); - if (rv < 0) { - return (int)rv; - } - - buf->last += rv; - - return 0; -} - -ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { - ngtcp2_buf *buf = &ppe->buf; - ngtcp2_crypto_cc *cc = ppe->cc; - uint8_t *payload = buf->begin + ppe->hdlen; - size_t payloadlen = ngtcp2_buf_len(buf) - ppe->hdlen; - uint8_t mask[NGTCP2_HP_SAMPLELEN]; - uint8_t *p; - size_t i; - int rv; - - assert(cc->encrypt); - assert(cc->hp_mask); - - if (ppe->len_offset) { - ngtcp2_put_varint14( - buf->begin + ppe->len_offset, - (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead_overhead)); - } - - ngtcp2_crypto_create_nonce(ppe->nonce, cc->ckm->iv.base, cc->ckm->iv.len, - ppe->pkt_num); - - rv = cc->encrypt(payload, &cc->aead, &cc->ckm->aead_ctx, payload, payloadlen, - ppe->nonce, cc->ckm->iv.len, buf->begin, ppe->hdlen); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - buf->last = payload + payloadlen + cc->aead_overhead; - - /* TODO Check that we have enough space to get sample */ - assert(ppe->sample_offset + NGTCP2_HP_SAMPLELEN <= ngtcp2_buf_len(buf)); - - rv = cc->hp_mask(mask, &cc->hp, &cc->hp_ctx, buf->begin + ppe->sample_offset); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - p = buf->begin; - if (*p & NGTCP2_HEADER_FORM_BIT) { - *p = (uint8_t)(*p ^ (mask[0] & 0x0f)); - } else { - *p = (uint8_t)(*p ^ (mask[0] & 0x1f)); - } - - p = buf->begin + ppe->pkt_num_offset; - for (i = 0; i < ppe->pkt_numlen; ++i) { - *(p + i) ^= mask[i + 1]; - } - - if (ppkt != NULL) { - *ppkt = buf->begin; - } - - return (ngtcp2_ssize)ngtcp2_buf_len(buf); -} - -size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe) { - ngtcp2_crypto_cc *cc = ppe->cc; - - if (ngtcp2_buf_left(&ppe->buf) < cc->aead_overhead) { - return 0; - } - - return ngtcp2_buf_left(&ppe->buf) - cc->aead_overhead; -} - -size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe) { - ngtcp2_crypto_cc *cc = ppe->cc; - - return ngtcp2_buf_len(&ppe->buf) + cc->aead_overhead; -} - -size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe) { - ngtcp2_crypto_cc *cc = ppe->cc; - ngtcp2_buf *buf = &ppe->buf; - size_t len; - - assert(ngtcp2_buf_left(buf) >= cc->aead_overhead); - - len = ngtcp2_buf_left(buf) - cc->aead_overhead; - memset(buf->last, 0, len); - buf->last += len; - - return len; -} - -size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe) { - ngtcp2_crypto_cc *cc = ppe->cc; - ngtcp2_buf *buf = &ppe->buf; - size_t max_samplelen; - size_t len = 0; - - assert(cc->aead_overhead); - - max_samplelen = ngtcp2_buf_len(buf) + cc->aead_overhead - ppe->sample_offset; - if (max_samplelen < NGTCP2_HP_SAMPLELEN) { - len = NGTCP2_HP_SAMPLELEN - max_samplelen; - assert(ngtcp2_ppe_left(ppe) >= len); - memset(buf->last, 0, len); - buf->last += len; - } - - return len; -} - -size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) { - ngtcp2_crypto_cc *cc = ppe->cc; - ngtcp2_buf *buf = &ppe->buf; - size_t pktlen = ngtcp2_buf_len(buf) + cc->aead_overhead; - size_t len; - - if (pktlen >= n) { - return 0; - } - - len = n - pktlen; - buf->last = ngtcp2_setmem(buf->last, 0, len); - - return len; -} - -int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe) { - ngtcp2_buf *buf = &ppe->buf; - return ngtcp2_buf_left(buf) >= (4 - ppe->pkt_numlen) + NGTCP2_HP_SAMPLELEN; -} diff --git a/deps/ngtcp2/lib/ngtcp2_ppe.h b/deps/ngtcp2/lib/ngtcp2_ppe.h deleted file mode 100644 index ca6b21dce0b..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_ppe.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_PPE_H -#define NGTCP2_PPE_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_pkt.h" -#include "ngtcp2_buf.h" -#include "ngtcp2_crypto.h" - -/* - * ngtcp2_ppe is the Protected Packet Encoder. - */ -typedef struct { - ngtcp2_buf buf; - ngtcp2_crypto_cc *cc; - /* hdlen is the number of bytes for packet header written in buf. */ - size_t hdlen; - /* len_offset is the offset to Length field. */ - size_t len_offset; - /* pkt_num_offset is the offset to packet number field. */ - size_t pkt_num_offset; - /* pkt_numlen is the number of bytes used to encode a packet - number */ - size_t pkt_numlen; - /* sample_offset is the offset to sample for packet number - encryption. */ - size_t sample_offset; - /* pkt_num is the packet number written in buf. */ - int64_t pkt_num; - /* nonce is the buffer to store nonce. It should be equal or longer - than then length of IV. */ - uint8_t nonce[32]; -} ngtcp2_ppe; - -/* - * ngtcp2_ppe_init initializes |ppe| with the given buffer. - */ -void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, - ngtcp2_crypto_cc *cc); - -/* - * ngtcp2_ppe_encode_hd encodes |hd|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOBUF - * The buffer is too small. - */ -int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd); - -/* - * ngtcp2_ppe_encode_frame encodes |fr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOBUF - * The buffer is too small. - */ -int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr); - -/* - * ngtcp2_ppe_final encrypts QUIC packet payload. If |**ppkt| is not - * NULL, the pointer to the packet is assigned to it. - * - * This function returns the length of QUIC packet, including header, - * and payload if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt); - -/* - * ngtcp2_ppe_left returns the number of bytes left to write - * additional frames. This does not count AEAD overhead. - */ -size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe); - -/* - * ngtcp2_ppe_pktlen returns the provisional packet length. It - * includes AEAD overhead. - */ -size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe); - -/** - * @function - * - * `ngtcp2_ppe_padding` encodes PADDING frames to the end of the - * buffer. This function returns the number of bytes padded. - */ -size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe); - -/* - * ngtcp2_ppe_padding_hp_sample adds PADDING frame if the current - * payload does not have enough space for header protection sample. - * This function should be called just before calling - * ngtcp2_ppe_final(). - * - * This function returns the number of bytes added as padding. - */ -size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe); - -/* - * ngtcp2_ppe_padding_size adds PADDING frame so that the size of QUIC - * packet is at least |n| bytes long. If it is unable to add PADDING - * in that way, this function still adds PADDING frame as much as - * possible. This function should be called just before calling - * ngtcp2_ppe_final(). For Short packet, this function should be - * called instead of ngtcp2_ppe_padding_hp_sample. - * - * This function returns the number of bytes added as padding. - */ -size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n); - -/* - * ngtcp2_ppe_ensure_hp_sample returns nonzero if the buffer has - * enough space for header protection sample. This should be called - * right after packet header is written. - */ -int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe); - -#endif /* NGTCP2_PPE_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pq.c b/deps/ngtcp2/lib/ngtcp2_pq.c deleted file mode 100644 index 5e1003d7942..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_pq.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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 "ngtcp2_pq.h" - -#include <assert.h> - -#include "ngtcp2_macro.h" - -void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem) { - pq->mem = mem; - pq->capacity = 0; - pq->q = NULL; - pq->length = 0; - pq->less = less; -} - -void ngtcp2_pq_free(ngtcp2_pq *pq) { - ngtcp2_mem_free(pq->mem, pq->q); - pq->q = NULL; -} - -static void swap(ngtcp2_pq *pq, size_t i, size_t j) { - ngtcp2_pq_entry *a = pq->q[i]; - ngtcp2_pq_entry *b = pq->q[j]; - - pq->q[i] = b; - b->index = i; - pq->q[j] = a; - a->index = j; -} - -static void bubble_up(ngtcp2_pq *pq, size_t index) { - size_t parent; - while (index != 0) { - parent = (index - 1) / 2; - if (!pq->less(pq->q[index], pq->q[parent])) { - return; - } - swap(pq, parent, index); - index = parent; - } -} - -int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item) { - if (pq->capacity <= pq->length) { - void *nq; - size_t ncapacity; - - ncapacity = ngtcp2_max(4, (pq->capacity * 2)); - - nq = ngtcp2_mem_realloc(pq->mem, pq->q, - ncapacity * sizeof(ngtcp2_pq_entry *)); - if (nq == NULL) { - return NGTCP2_ERR_NOMEM; - } - pq->capacity = ncapacity; - pq->q = nq; - } - pq->q[pq->length] = item; - item->index = pq->length; - ++pq->length; - bubble_up(pq, pq->length - 1); - return 0; -} - -ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq) { - assert(pq->length); - return pq->q[0]; -} - -static void bubble_down(ngtcp2_pq *pq, size_t index) { - size_t i, j, minindex; - for (;;) { - j = index * 2 + 1; - minindex = index; - for (i = 0; i < 2; ++i, ++j) { - if (j >= pq->length) { - break; - } - if (pq->less(pq->q[j], pq->q[minindex])) { - minindex = j; - } - } - if (minindex == index) { - return; - } - swap(pq, index, minindex); - index = minindex; - } -} - -void ngtcp2_pq_pop(ngtcp2_pq *pq) { - if (pq->length > 0) { - pq->q[0] = pq->q[pq->length - 1]; - pq->q[0]->index = 0; - --pq->length; - bubble_down(pq, 0); - } -} - -void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item) { - assert(pq->q[item->index] == item); - - if (item->index == 0) { - ngtcp2_pq_pop(pq); - return; - } - - if (item->index == pq->length - 1) { - --pq->length; - return; - } - - pq->q[item->index] = pq->q[pq->length - 1]; - pq->q[item->index]->index = item->index; - --pq->length; - - if (pq->less(item, pq->q[item->index])) { - bubble_down(pq, item->index); - } else { - bubble_up(pq, item->index); - } -} - -int ngtcp2_pq_empty(ngtcp2_pq *pq) { return pq->length == 0; } - -size_t ngtcp2_pq_size(ngtcp2_pq *pq) { return pq->length; } - -int ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_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; -} diff --git a/deps/ngtcp2/lib/ngtcp2_pq.h b/deps/ngtcp2/lib/ngtcp2_pq.h deleted file mode 100644 index b7e4a77ba5b..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_pq.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 ngtcp2 contributors - * Copyright (c) 2012 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. - */ -#ifndef NGTCP2_PQ_H -#define NGTCP2_PQ_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" - -/* Implementation of priority queue */ - -/* NGTCP2_PQ_BAD_INDEX is the priority queue index which indicates - that an entry is not queued. Assigning this value to - ngtcp2_pq_entry.index can check that the entry is queued or not. */ -#define NGTCP2_PQ_BAD_INDEX SIZE_MAX - -typedef struct { - size_t index; -} ngtcp2_pq_entry; - -/* "less" function, return nonzero if |lhs| is less than |rhs|. */ -typedef int (*ngtcp2_less)(const ngtcp2_pq_entry *lhs, - const ngtcp2_pq_entry *rhs); - -typedef struct { - /* The pointer to the pointer to the item stored */ - ngtcp2_pq_entry **q; - /* Memory allocator */ - const ngtcp2_mem *mem; - /* The number of items stored */ - size_t length; - /* The maximum number of items this pq can store. This is - automatically extended when length is reached to this value. */ - size_t capacity; - /* The less function between items */ - ngtcp2_less less; -} ngtcp2_pq; - -/* - * Initializes priority queue |pq| with compare function |cmp|. - */ -void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem); - -/* - * Deallocates any resources allocated for |pq|. The stored items are - * not freed by this function. - */ -void ngtcp2_pq_free(ngtcp2_pq *pq); - -/* - * Adds |item| to the priority queue |pq|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item); - -/* - * Returns item at the top of the queue |pq|. It is undefined if the - * queue is empty. - */ -ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq); - -/* - * Pops item at the top of the queue |pq|. The popped item is not - * freed by this function. - */ -void ngtcp2_pq_pop(ngtcp2_pq *pq); - -/* - * Returns nonzero if the queue |pq| is empty. - */ -int ngtcp2_pq_empty(ngtcp2_pq *pq); - -/* - * Returns the number of items in the queue |pq|. - */ -size_t ngtcp2_pq_size(ngtcp2_pq *pq); - -typedef int (*ngtcp2_pq_item_cb)(ngtcp2_pq_entry *item, void *arg); - -/* - * Applys |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 ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg); - -/* - * Removes |item| from priority queue. - */ -void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item); - -#endif /* NGTCP2_PQ_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/lib/ngtcp2_pv.c deleted file mode 100644 index 171f02dc43d..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_pv.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_pv.h" - -#include <string.h> - -#include "ngtcp2_mem.h" -#include "ngtcp2_log.h" -#include "ngtcp2_macro.h" -#include "ngtcp2_addr.h" - -void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, - ngtcp2_tstamp expiry) { - memcpy(pvent->data, data, sizeof(pvent->data)); - pvent->expiry = expiry; -} - -int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, - ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, - const ngtcp2_mem *mem) { - int rv; - - (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv)); - if (*ppv == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES, - sizeof(ngtcp2_pv_entry), mem); - if (rv != 0) { - ngtcp2_mem_free(mem, *ppv); - return 0; - } - - ngtcp2_dcid_copy(&(*ppv)->dcid, dcid); - - (*ppv)->mem = mem; - (*ppv)->log = log; - (*ppv)->timeout = timeout; - (*ppv)->started_ts = 0; - (*ppv)->loss_count = 0; - (*ppv)->flags = flags; - - return 0; -} - -void ngtcp2_pv_del(ngtcp2_pv *pv) { - if (pv == NULL) { - return; - } - ngtcp2_ringbuf_free(&pv->ents); - ngtcp2_mem_free(pv->mem, pv); -} - -void ngtcp2_pv_ensure_start(ngtcp2_pv *pv, ngtcp2_tstamp ts) { - if (pv->started_ts) { - return; - } - pv->started_ts = ts; -} - -void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, - ngtcp2_tstamp expiry) { - ngtcp2_pv_entry *ent = ngtcp2_ringbuf_push_back(&pv->ents); - ngtcp2_pv_entry_init(ent, data, expiry); -} - -int ngtcp2_pv_full(ngtcp2_pv *pv) { return ngtcp2_ringbuf_full(&pv->ents); } - -int ngtcp2_pv_validate(ngtcp2_pv *pv, const uint8_t *data) { - size_t len = ngtcp2_ringbuf_len(&pv->ents); - size_t i; - ngtcp2_pv_entry *ent; - - if (len == 0) { - return NGTCP2_ERR_INVALID_STATE; - } - - for (i = 0; i < len; ++i) { - ent = ngtcp2_ringbuf_get(&pv->ents, i); - if (memcmp(ent->data, data, sizeof(ent->data)) == 0) { - ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated"); - return 0; - } - } - - return NGTCP2_ERR_INVALID_ARGUMENT; -} - -void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) { - ngtcp2_pv_entry *ent; - - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { - return; - } - - ent = ngtcp2_ringbuf_get(&pv->ents, 0); - if (ent->expiry <= ts) { - ++pv->loss_count; - - ngtcp2_ringbuf_pop_front(&pv->ents); - - for (; ngtcp2_ringbuf_len(&pv->ents);) { - ent = ngtcp2_ringbuf_get(&pv->ents, 0); - if (ent->expiry <= ts) { - ngtcp2_ringbuf_pop_front(&pv->ents); - continue; - } - break; - } - } -} - -int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) { - return pv->started_ts + pv->timeout <= ts; -} - -ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) { - ngtcp2_tstamp t = UINT64_MAX; - ngtcp2_pv_entry *ent; - - if (pv->started_ts) { - t = pv->started_ts + pv->timeout; - } - - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { - return t; - } - - ent = ngtcp2_ringbuf_get(&pv->ents, 0); - - return ngtcp2_min(t, ent->expiry); -} diff --git a/deps/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/lib/ngtcp2_pv.h deleted file mode 100644 index 0f1734ec3e1..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_pv.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_PV_H -#define NGTCP2_PV_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_cid.h" -#include "ngtcp2_ringbuf.h" - -/* NGTCP2_PV_MAX_ENTRIES is the maximum number of entries that - ngtcp2_pv can contain. It must be power of 2. */ -#define NGTCP2_PV_MAX_ENTRIES 4 - -struct ngtcp2_log; -typedef struct ngtcp2_log ngtcp2_log; - -struct ngtcp2_frame_chain; -typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; - -typedef struct { - /* expiry is the timestamp when this PATH_CHALLENGE expires. */ - ngtcp2_tstamp expiry; - /* data is a byte string included in PATH_CHALLENGE. */ - uint8_t data[8]; -} ngtcp2_pv_entry; - -void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, - ngtcp2_tstamp expiry); - -typedef enum { - NGTCP2_PV_FLAG_NONE, - /* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID - is available in ngtcp2_pv. If path validation fails, fallback to - the fallback DCID. If path validation succeeds, fallback DCID is - retired if it does not equal to the current DCID. */ - NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE = 0x04, -} ngtcp2_pv_flag; - -struct ngtcp2_pv; -typedef struct ngtcp2_pv ngtcp2_pv; - -/* - * ngtcp2_pv is the context of a single path validation. - */ -struct ngtcp2_pv { - const ngtcp2_mem *mem; - ngtcp2_log *log; - /* dcid is DCID and path this path validation uses. */ - ngtcp2_dcid dcid; - /* fallback_dcid is the usually validated DCID and used as a - fallback if this path validation fails. */ - ngtcp2_dcid fallback_dcid; - /* ents is the ring buffer of ngtcp2_pv_entry */ - ngtcp2_ringbuf ents; - /* timeout is the duration within which this path validation should - succeed. */ - ngtcp2_duration timeout; - /* started_ts is the timestamp this path validation starts. */ - ngtcp2_tstamp started_ts; - /* loss_count is the number of lost PATH_CHALLENGE */ - size_t loss_count; - /* flags is bitwise-OR of zero or more of ngtcp2_pv_flag. */ - uint8_t flags; -}; - -/* - * ngtcp2_pv_new creates new ngtcp2_pv object and assigns its pointer - * to |*ppv|. This function makes a copy of |dcid|. |timeout| is a - * duration within which this path validation must succeed. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, - ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, - const ngtcp2_mem *mem); - -/* - * ngtcp2_pv_del deallocates |pv|. This function frees memory |pv| - * points too. - */ -void ngtcp2_pv_del(ngtcp2_pv *pv); - -/* - * ngtcp2_pv_ensure_start sets started_ts field to |ts| if it is zero. - */ -void ngtcp2_pv_ensure_start(ngtcp2_pv *pv, ngtcp2_tstamp ts); - -/* - * ngtcp2_pv_add_entry adds new entry with |data|. |expiry| is the - * expiry time of the entry. - */ -void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, - ngtcp2_tstamp expiry); - -/* - * ngtcp2_pv_full returns nonzero if |pv| is full of ngtcp2_pv_entry. - */ -int ngtcp2_pv_full(ngtcp2_pv *pv); - -/* - * ngtcp2_pv_validate validates that the received |data| matches the - * one of the existing entry. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_PATH_VALIDATION_FAILED - * path validation has failed and must be abandoned - * NGTCP2_ERR_INVALID_STATE - * |pv| includes no entry - * NGTCP2_ERR_INVALID_ARGUMENT - * |pv| does not have an entry which has |data| and |path| - */ -int ngtcp2_pv_validate(ngtcp2_pv *pv, const uint8_t *data); - -/* - * ngtcp2_pv_handle_entry_expiry checks expiry for each entry. - */ -void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts); - -/* - * ngtcp2_pv_validation_timed_out returns nonzero if the path - * validation fails because of timeout. - */ -int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts); - -/* - * ngtcp2_pv_next_expiry returns the earliest expiry. - */ -ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv); - -#endif /* NGTCP2_PV_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/lib/ngtcp2_qlog.c deleted file mode 100644 index 580b6a6fe0f..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_qlog.c +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_qlog.h" - -#include <assert.h> - -#include "ngtcp2_str.h" -#include "ngtcp2_vec.h" - -void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, - ngtcp2_tstamp ts, void *user_data) { - qlog->write = write; - qlog->ts = qlog->last_ts = ts; - qlog->user_data = user_data; -} - -static uint8_t *write_string(uint8_t *p, const ngtcp2_vec *s) { - *p++ = '"'; - if (s->len) { - p = ngtcp2_cpymem(p, s->base, s->len); - } - *p++ = '"'; - return p; -} - -#define NGTCP2_LOWER_XDIGITS "0123456789abcdef" - -static uint8_t *write_hex(uint8_t *p, const ngtcp2_vec *s) { - const uint8_t *b = s->base, *end = s->base + s->len; - *p++ = '"'; - for (; b != end; ++b) { - *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b >> 4]; - *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b & 0xf]; - } - *p++ = '"'; - return p; -} - -static uint8_t *write_cid(uint8_t *p, const ngtcp2_cid *cid) { - ngtcp2_vec value; - return write_hex(p, ngtcp2_vec_init(&value, cid->data, cid->datalen)); -} - -static uint8_t *write_number(uint8_t *p, uint64_t n) { - size_t nlen = 0; - uint64_t t; - uint8_t *res; - - if (n == 0) { - *p++ = '0'; - return p; - } - for (t = n; t; t /= 10, ++nlen) - ; - p += nlen; - res = p; - for (; n; n /= 10) { - *--p = (uint8_t)((n % 10) + '0'); - } - return res; -} - -static uint8_t *write_numstr(uint8_t *p, uint64_t n) { - *p++ = '"'; - p = write_number(p, n); - *p++ = '"'; - return p; -} - -static uint8_t *write_tstamp(uint8_t *p, ngtcp2_tstamp ts) { - return write_number(p, ts / NGTCP2_MILLISECONDS); -} - -static uint8_t *write_duration(uint8_t *p, ngtcp2_duration duration) { - return write_number(p, duration / NGTCP2_MILLISECONDS); -} - -static uint8_t *write_bool(uint8_t *p, int b) { - if (b) { - return ngtcp2_cpymem(p, "true", sizeof("true") - 1); - } - return ngtcp2_cpymem(p, "false", sizeof("false") - 1); -} - -static uint8_t *write_pair(uint8_t *p, const ngtcp2_vec *name, - const ngtcp2_vec *value) { - p = write_string(p, name); - *p++ = ':'; - return write_string(p, value); -} - -static uint8_t *write_pair_hex(uint8_t *p, const ngtcp2_vec *name, - const ngtcp2_vec *value) { - p = write_string(p, name); - *p++ = ':'; - return write_hex(p, value); -} - -static uint8_t *write_pair_numstr(uint8_t *p, const ngtcp2_vec *name, - uint64_t value) { - p = write_string(p, name); - *p++ = ':'; - p = write_numstr(p, value); - return p; -} - -static uint8_t *write_pair_number(uint8_t *p, const ngtcp2_vec *name, - uint64_t value) { - p = write_string(p, name); - *p++ = ':'; - return write_number(p, value); -} - -static uint8_t *write_pair_duration(uint8_t *p, const ngtcp2_vec *name, - ngtcp2_tstamp duration) { - p = write_string(p, name); - *p++ = ':'; - return write_duration(p, duration); -} - -static uint8_t *write_pair_bool(uint8_t *p, const ngtcp2_vec *name, int b) { - p = write_string(p, name); - *p++ = ':'; - return write_bool(p, b); -} - -static uint8_t *write_pair_cid(uint8_t *p, const ngtcp2_vec *name, - const ngtcp2_cid *cid) { - p = write_string(p, name); - *p++ = ':'; - return write_cid(p, cid); -} - -static uint8_t *write_trace_start(uint8_t *p, int server) { -#define NGTCP2_M \ - "\"traces\":[{\"vantage_point\":{\"name\":\"ngtcp2\",\"type\":\"" - p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); -#undef NGTCP2_M - if (server) { - p = ngtcp2_cpymem(p, "server", sizeof("server") - 1); - } else { - p = ngtcp2_cpymem(p, "client", sizeof("client") - 1); - } - *p++ = '"'; - *p++ = '}'; - *p++ = ','; - return p; -} - -static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) { - ngtcp2_vec name; -#define NGTCP2_M \ - "\"common_fields\":{\"protocol_type\":\"QUIC_HTTP3\",\"reference_" \ - "time\":\"0\"," - p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); -#undef NGTCP2_M - p = write_pair_cid(p, ngtcp2_vec_lit(&name, "group_id"), odcid); - *p++ = ','; - p = write_pair_cid(p, ngtcp2_vec_lit(&name, "ODCID"), odcid); - *p++ = '}'; - *p++ = ','; - return p; -} - -static uint8_t *write_event_fields(uint8_t *p) { -#define NGTCP2_M \ - "\"event_fields\":[\"relative_time\",\"category\",\"event\",\"data\"]," - p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); -#undef NGTCP2_M - return p; -} - -static uint8_t *write_events_start(uint8_t *p) { -#define NGTCP2_M "\"events\":[" - p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); -#undef NGTCP2_M - return p; -} - -static uint8_t *write_events_end(uint8_t *p) { - *p++ = '['; - *p++ = ']'; - *p++ = ']'; - return p; -} - -static uint8_t *write_trace_end(uint8_t *p) { - *p++ = '}'; - *p++ = ']'; - return p; -} - -void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) { - uint8_t buf[1024]; - ngtcp2_vec name, value; - uint8_t *p = buf; - - if (!qlog->write) { - return; - } - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "qlog_version"), - ngtcp2_vec_lit(&value, "draft-01")); - *p++ = ','; - p = write_trace_start(p, server); - p = write_common_fields(p, odcid); - p = write_event_fields(p); - p = write_events_start(p); - - qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, - (size_t)(p - buf)); -} - -void ngtcp2_qlog_end(ngtcp2_qlog *qlog) { - uint8_t buf[256]; - uint8_t *p = buf; - - if (!qlog->write) { - return; - } - - p = write_events_end(p); - p = write_trace_end(p); - *p++ = '}'; - - qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_FIN, buf, - (size_t)(p - buf)); -} - -static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd, - size_t pktlen) { - ngtcp2_vec value; - - /* - * {"packet_number":"0000000000000000000","packet_size":0000000000000000000} - */ -#define NGTCP2_QLOG_PKT_HD_OVERHEAD 73 - - *p++ = '{'; - p = write_pair_numstr(p, ngtcp2_vec_lit(&value, "packet_number"), - (uint64_t)hd->pkt_num); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&value, "packet_size"), pktlen); - /* TODO Write DCIL and DCID */ - /* TODO Write SCIL and SCID */ - *p++ = '}'; - return p; -} - -static ngtcp2_vec *qlog_pkt_type(ngtcp2_vec *dest, const ngtcp2_pkt_hd *hd) { - if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { - switch (hd->type) { - case NGTCP2_PKT_INITIAL: - return ngtcp2_vec_lit(dest, "initial"); - case NGTCP2_PKT_HANDSHAKE: - return ngtcp2_vec_lit(dest, "handshake"); - case NGTCP2_PKT_0RTT: - return ngtcp2_vec_lit(dest, "0RTT"); - default: - return ngtcp2_vec_lit(dest, "unknown"); - } - } - - return ngtcp2_vec_lit(dest, "1RTT"); -} - -static uint8_t *write_padding_frame(uint8_t *p, const ngtcp2_padding *fr) { - ngtcp2_vec name, value; - (void)fr; - - /* {"frame_type":"padding"} */ -#define NGTCP2_QLOG_PADDING_FRAME_OVERHEAD 24 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "padding")); - *p++ = '}'; - - return p; -} - -static uint8_t *write_ping_frame(uint8_t *p, const ngtcp2_ping *fr) { - ngtcp2_vec name, value; - (void)fr; - - /* {"frame_type":"ping"} */ -#define NGTCP2_QLOG_PING_FRAME_OVERHEAD 21 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "ping")); - *p++ = '}'; - - return p; -} - -static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) { - ngtcp2_vec name, value; - int64_t largest_ack, min_ack; - size_t i; - const ngtcp2_ack_blk *blk; - - /* - * {"frame_type":"ack","ack_delay":0000000000000000000,"acked_ranges":[]} - * - * each range: - * ["0000000000000000000","0000000000000000000"], - */ -#define NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD 70 -#define NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD 46 - - *p++ = '{'; - /* TODO Handle ACK ECN */ - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "ack")); - *p++ = ','; - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "ack_delay"), - fr->ack_delay_unscaled); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&name, "acked_ranges")); - *p++ = ':'; - *p++ = '['; - - largest_ack = fr->largest_ack; - min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; - - *p++ = '['; - p = write_numstr(p, (uint64_t)min_ack); - if (largest_ack != min_ack) { - *p++ = ','; - p = write_numstr(p, (uint64_t)largest_ack); - } - *p++ = ']'; - - for (i = 0; i < fr->num_blks; ++i) { - blk = &fr->blks[i]; - largest_ack = min_ack - (int64_t)blk->gap - 2; - min_ack = largest_ack - (int64_t)blk->blklen; - *p++ = ','; - *p++ = '['; - p = write_numstr(p, (uint64_t)min_ack); - if (largest_ack != min_ack) { - *p++ = ','; - p = write_numstr(p, (uint64_t)largest_ack); - } - *p++ = ']'; - } - - *p++ = ']'; - *p++ = '}'; - - return p; -} - -static uint8_t *write_reset_stream_frame(uint8_t *p, - const ngtcp2_reset_stream *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"reset_stream","stream_id":"0000000000000000000","error_code":0000000000000000000,"final_size":"0000000000000000000"} - */ -#define NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD 131 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "reset_stream")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), - (uint64_t)fr->stream_id); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "error_code"), - fr->app_error_code); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "final_size"), fr->final_size); - *p++ = '}'; - - return p; -} - -static uint8_t *write_stop_sending_frame(uint8_t *p, - const ngtcp2_stop_sending *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"stop_sending","stream_id":"0000000000000000000","error_code":0000000000000000000} - */ -#define NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD 96 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "stop_sending")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), - (uint64_t)fr->stream_id); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "error_code"), - fr->app_error_code); - *p++ = '}'; - - return p; -} - -static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"crypto","offset":"0000000000000000000","length":0000000000000000000} - */ -#define NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD 83 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "crypto")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "offset"), fr->offset); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "length"), - ngtcp2_vec_len(fr->data, fr->datacnt)); - *p++ = '}'; - - return p; -} - -static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) { - ngtcp2_vec name, value; - (void)fr; - - /* - * {"frame_type":"new_token","length":0000000000000000000,"token":""} - */ -#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "new_token")); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "length"), fr->token.len); - *p++ = ','; - p = write_pair_hex(p, ngtcp2_vec_lit(&name, "token"), &fr->token); - *p++ = '}'; - - return p; -} - -static uint8_t *write_stream_frame(uint8_t *p, const ngtcp2_stream *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"stream","stream_id":"0000000000000000000","offset":"0000000000000000000","length":0000000000000000000,"fin":true} - */ -#define NGTCP2_QLOG_STREAM_FRAME_OVERHEAD 128 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "stream")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), - (uint64_t)fr->stream_id); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "offset"), fr->offset); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "length"), - ngtcp2_vec_len(fr->data, fr->datacnt)); - if (fr->fin) { - *p++ = ','; - p = write_pair_bool(p, ngtcp2_vec_lit(&name, "fin"), 1); - } - *p++ = '}'; - - return p; -} - -static uint8_t *write_max_data_frame(uint8_t *p, const ngtcp2_max_data *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"max_data","maximum":"0000000000000000000"} - */ -#define NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD 57 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "max_data")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "maximum"), fr->max_data); - *p++ = '}'; - - return p; -} - -static uint8_t *write_max_stream_data_frame(uint8_t *p, - const ngtcp2_max_stream_data *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"max_stream_data","stream_id":"0000000000000000000","maximum":"0000000000000000000"} - */ -#define NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD 98 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "max_stream_data")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), - (uint64_t)fr->stream_id); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "maximum"), - fr->max_stream_data); - *p++ = '}'; - - return p; -} - -static uint8_t *write_max_streams_frame(uint8_t *p, - const ngtcp2_max_streams *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"max_streams","stream_type":"unidirectional","maximum":"0000000000000000000"} - */ -#define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 91 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "max_streams")); - *p++ = ','; - p = write_pair(p, ngtcp2_vec_lit(&name, "stream_type"), - fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI - ? ngtcp2_vec_lit(&value, "bidirectional") - : ngtcp2_vec_lit(&value, "unidirectional")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "maximum"), fr->max_streams); - *p++ = '}'; - - return p; -} - -static uint8_t *write_data_blocked_frame(uint8_t *p, - const ngtcp2_data_blocked *fr) { - ngtcp2_vec name, value; - (void)fr; - - /* - * {"frame_type":"data_blocked"} - */ -#define NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD 29 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "data_blocked")); - *p++ = '}'; - - return p; -} - -static uint8_t * -write_stream_data_blocked_frame(uint8_t *p, - const ngtcp2_stream_data_blocked *fr) { - ngtcp2_vec name, value; - (void)fr; - - /* - * {"frame_type":"stream_data_blocked"} - */ -#define NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD 36 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "stream_data_blocked")); - *p++ = '}'; - - return p; -} - -static uint8_t *write_streams_blocked_frame(uint8_t *p, - const ngtcp2_streams_blocked *fr) { - ngtcp2_vec name, value; - (void)fr; - - /* - * {"frame_type":"streams_blocked"} - */ -#define NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD 32 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "streams_blocked")); - *p++ = '}'; - - return p; -} - -static uint8_t * -write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"new_connection_id","sequence_number":"0000000000000000000","retire_prior_to":"0000000000000000000","length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} - */ -#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 251 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "new_connection_id")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "sequence_number"), fr->seq); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "retire_prior_to"), - fr->retire_prior_to); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "length"), fr->cid.datalen); - *p++ = ','; - p = write_pair_cid(p, ngtcp2_vec_lit(&name, "connection_id"), &fr->cid); - *p++ = ','; - p = write_pair_hex(p, ngtcp2_vec_lit(&name, "reset_token"), - ngtcp2_vec_init(&value, fr->stateless_reset_token, - sizeof(fr->stateless_reset_token))); - *p++ = '}'; - - return p; -} - -static uint8_t * -write_retire_connection_id_frame(uint8_t *p, - const ngtcp2_retire_connection_id *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"retire_connection_id","sequence_number":"0000000000000000000"} - */ -#define NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD 77 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "retire_connection_id")); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "sequence_number"), fr->seq); - *p++ = '}'; - - return p; -} - -static uint8_t *write_path_challenge_frame(uint8_t *p, - const ngtcp2_path_challenge *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"path_challenge","data":"xxxxxxxxxxxxxxxx"} - */ -#define NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD 57 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "path_challenge")); - *p++ = ','; - p = write_pair_hex(p, ngtcp2_vec_lit(&name, "data"), - ngtcp2_vec_init(&value, fr->data, sizeof(fr->data))); - *p++ = '}'; - - return p; -} - -static uint8_t *write_path_response_frame(uint8_t *p, - const ngtcp2_path_response *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"path_response","data":"xxxxxxxxxxxxxxxx"} - */ -#define NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD 56 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "path_response")); - *p++ = ','; - p = write_pair_hex(p, ngtcp2_vec_lit(&name, "data"), - ngtcp2_vec_init(&value, fr->data, sizeof(fr->data))); - *p++ = '}'; - - return p; -} - -static uint8_t * -write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { - ngtcp2_vec name, value; - - /* - * {"frame_type":"connection_close","error_space":"application","error_code":0000000000000000000,"raw_error_code":0000000000000000000,"reason":""} - */ -#define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 143 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "connection_close")); - *p++ = ','; - p = write_pair(p, ngtcp2_vec_lit(&name, "error_space"), - fr->type == NGTCP2_FRAME_CONNECTION_CLOSE - ? ngtcp2_vec_lit(&value, "transport") - : ngtcp2_vec_lit(&value, "application")); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "error_code"), fr->error_code); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "raw_error_code"), - fr->error_code); - *p++ = ','; - /* TODO Write reason by escaping non-printables */ - p = write_pair(p, ngtcp2_vec_lit(&name, "reason"), - ngtcp2_vec_lit(&value, "")); - /* TODO Write trigger_frame_type */ - *p++ = '}'; - - return p; -} - -static uint8_t *write_handshake_done_frame(uint8_t *p, - const ngtcp2_handshake_done *fr) { - ngtcp2_vec name, value; - (void)fr; - - /* - * {"frame_type":"handshake_done"} - */ -#define NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD 31 - - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), - ngtcp2_vec_lit(&value, "handshake_done")); - *p++ = '}'; - - return p; -} - -static void qlog_pkt_write_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, - int sent) { - uint8_t *p; - ngtcp2_vec name, value; - - if (!qlog->write) { - return; - } - - ngtcp2_buf_reset(&qlog->buf); - p = qlog->buf.last; - - *p++ = '['; - p = write_tstamp(p, qlog->last_ts - qlog->ts); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "transport")); - *p++ = ','; - p = write_string(p, sent ? ngtcp2_vec_lit(&value, "packet_sent") - : ngtcp2_vec_lit(&value, "packet_received")); - *p++ = ','; - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "packet_type"), - qlog_pkt_type(&value, hd)); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "frames")); - *p++ = ':'; - *p++ = '['; - - qlog->buf.last = p; -} - -static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, - size_t pktlen) { - uint8_t *p = qlog->buf.last; - ngtcp2_vec value; - - if (!qlog->write) { - return; - } - - /* - * ],"header":}], - */ -#define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD (14 + NGTCP2_QLOG_PKT_HD_OVERHEAD) - - assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD); - assert(ngtcp2_buf_len(&qlog->buf)); - - /* Eat last ',' */ - if (*(p - 1) == ',') { - --p; - } - *p++ = ']'; - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "header")); - *p++ = ':'; - p = write_pkt_hd(p, hd, pktlen); - *p++ = '}'; - *p++ = ']'; - *p++ = ','; - - qlog->buf.last = p; - - qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, qlog->buf.pos, - ngtcp2_buf_len(&qlog->buf)); -} - -void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { - uint8_t *p = qlog->buf.last; - - if (!qlog->write) { - return; - } - - switch (fr->type) { - case NGTCP2_FRAME_PADDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_padding_frame(p, &fr->padding); - break; - case NGTCP2_FRAME_PING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_ping_frame(p, &fr->ping); - break; - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD + - NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_ack_frame(p, &fr->ack); - break; - case NGTCP2_FRAME_RESET_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_reset_stream_frame(p, &fr->reset_stream); - break; - case NGTCP2_FRAME_STOP_SENDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_stop_sending_frame(p, &fr->stop_sending); - break; - case NGTCP2_FRAME_CRYPTO: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_crypto_frame(p, &fr->crypto); - break; - case NGTCP2_FRAME_NEW_TOKEN: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + - fr->new_token.token.len * 2 + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_new_token_frame(p, &fr->new_token); - break; - case NGTCP2_FRAME_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_stream_frame(p, &fr->stream); - break; - case NGTCP2_FRAME_MAX_DATA: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_max_data_frame(p, &fr->max_data); - break; - case NGTCP2_FRAME_MAX_STREAM_DATA: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_max_stream_data_frame(p, &fr->max_stream_data); - break; - case NGTCP2_FRAME_MAX_STREAMS_BIDI: - case NGTCP2_FRAME_MAX_STREAMS_UNI: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_max_streams_frame(p, &fr->max_streams); - break; - case NGTCP2_FRAME_DATA_BLOCKED: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_data_blocked_frame(p, &fr->data_blocked); - break; - case NGTCP2_FRAME_STREAM_DATA_BLOCKED: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked); - break; - case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: - case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_streams_blocked_frame(p, &fr->streams_blocked); - break; - case NGTCP2_FRAME_NEW_CONNECTION_ID: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_new_connection_id_frame(p, &fr->new_connection_id); - break; - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_retire_connection_id_frame(p, &fr->retire_connection_id); - break; - case NGTCP2_FRAME_PATH_CHALLENGE: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_path_challenge_frame(p, &fr->path_challenge); - break; - case NGTCP2_FRAME_PATH_RESPONSE: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_path_response_frame(p, &fr->path_response); - break; - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_connection_close_frame(p, &fr->connection_close); - break; - case NGTCP2_FRAME_HANDSHAKE_DONE: - if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { - return; - } - p = write_handshake_done_frame(p, &fr->handshake_done); - break; - default: - assert(0); - } - - *p++ = ','; - - qlog->buf.last = p; -} - -void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog, - const ngtcp2_pkt_hd *hd) { - qlog_pkt_write_start(qlog, hd, /* sent = */ 0); -} - -void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, - size_t pktlen) { - qlog_pkt_write_end(qlog, hd, pktlen); -} - -void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd) { - qlog_pkt_write_start(qlog, hd, /* sent = */ 1); -} - -void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, - size_t pktlen) { - qlog_pkt_write_end(qlog, hd, pktlen); -} - -void ngtcp2_qlog_parameters_set_transport_params( - ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server, - ngtcp2_qlog_side side) { - uint8_t buf[1024]; - uint8_t *p = buf; - ngtcp2_vec name, value; - const ngtcp2_preferred_addr *paddr; - - if (!qlog->write) { - return; - } - - *p++ = '['; - p = write_tstamp(p, qlog->last_ts - qlog->ts); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "transport")); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "parameters_set")); - *p++ = ','; - *p++ = '{'; - p = write_pair(p, ngtcp2_vec_lit(&name, "owner"), - side == NGTCP2_QLOG_SIDE_LOCAL - ? ngtcp2_vec_lit(&value, "local") - : ngtcp2_vec_lit(&value, "remote")); - *p++ = ','; - p = write_pair_cid(p, ngtcp2_vec_lit(&name, "initial_source_connection_id"), - ¶ms->initial_scid); - *p++ = ','; - if (side == (server ? NGTCP2_QLOG_SIDE_LOCAL : NGTCP2_QLOG_SIDE_REMOTE)) { - p = write_pair_cid( - p, ngtcp2_vec_lit(&name, "original_destination_connection_id"), - ¶ms->original_dcid); - *p++ = ','; - } - if (params->retry_scid_present) { - p = write_pair_cid(p, ngtcp2_vec_lit(&name, "retry_source_connection_id"), - ¶ms->retry_scid); - *p++ = ','; - } - if (params->stateless_reset_token_present) { - p = write_pair_hex(p, ngtcp2_vec_lit(&name, "stateless_reset_token"), - ngtcp2_vec_init(&value, params->stateless_reset_token, - sizeof(params->stateless_reset_token))); - *p++ = ','; - } - p = write_pair_bool(p, ngtcp2_vec_lit(&name, "disable_active_migration"), - params->disable_active_migration); - *p++ = ','; - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "max_idle_timeout"), - params->max_idle_timeout); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "max_udp_payload_size"), - params->max_udp_payload_size); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "ack_delay_exponent"), - params->ack_delay_exponent); - *p++ = ','; - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "max_ack_delay"), - params->max_ack_delay); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "active_connection_id_limit"), - params->active_connection_id_limit); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_data"), - params->initial_max_data); - *p++ = ','; - p = write_pair_numstr( - p, ngtcp2_vec_lit(&name, "initial_max_stream_data_bidi_local"), - params->initial_max_stream_data_bidi_local); - *p++ = ','; - p = write_pair_numstr( - p, ngtcp2_vec_lit(&name, "initial_max_stream_data_bidi_remote"), - params->initial_max_stream_data_bidi_remote); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_stream_data_uni"), - params->initial_max_stream_data_uni); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_streams_bidi"), - params->initial_max_streams_bidi); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_streams_uni"), - params->initial_max_streams_uni); - if (params->preferred_address_present) { - *p++ = ','; - paddr = ¶ms->preferred_address; - p = write_string(p, ngtcp2_vec_lit(&name, "preferred_address")); - *p++ = ':'; - *p++ = '{'; - p = write_pair_hex( - p, ngtcp2_vec_lit(&name, "ip_v4"), - ngtcp2_vec_init(&value, paddr->ipv4_addr, sizeof(paddr->ipv4_addr))); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "port_v4"), - paddr->ipv4_port); - *p++ = ','; - p = write_pair_hex( - p, ngtcp2_vec_lit(&name, "ip_v6"), - ngtcp2_vec_init(&value, paddr->ipv6_addr, sizeof(paddr->ipv6_addr))); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "port_v6"), - paddr->ipv6_port); - *p++ = ','; - p = write_pair_cid(p, ngtcp2_vec_lit(&name, "connection_id"), &paddr->cid); - *p++ = ','; - p = write_pair_hex(p, ngtcp2_vec_lit(&name, "stateless_reset_token"), - ngtcp2_vec_init(&value, paddr->stateless_reset_token, - sizeof(paddr->stateless_reset_token))); - *p++ = '}'; - } - *p++ = '}'; - *p++ = ']'; - *p++ = ','; - - qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, - (size_t)(p - buf)); -} - -void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, - const ngtcp2_conn_stat *cstat) { - uint8_t buf[1024]; - uint8_t *p = buf; - ngtcp2_vec name, value; - - if (!qlog->write) { - return; - } - - *p++ = '['; - p = write_tstamp(p, qlog->last_ts - qlog->ts); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "recovery")); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "metrics_updated")); - *p++ = ','; - *p++ = '{'; - - if (cstat->min_rtt != UINT64_MAX) { - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "min_rtt"), - cstat->min_rtt); - *p++ = ','; - } - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "smoothed_rtt"), - cstat->smoothed_rtt); - *p++ = ','; - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "latest_rtt"), - cstat->latest_rtt); - *p++ = ','; - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "rtt_variance"), - cstat->rttvar); - *p++ = ','; - /* TODO max_ack_delay? */ - p = write_pair_number(p, ngtcp2_vec_lit(&name, "pto_count"), - cstat->pto_count); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "congestion_window"), - cstat->cwnd); - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "bytes_in_flight"), - cstat->bytes_in_flight); - if (cstat->ssthresh != UINT64_MAX) { - *p++ = ','; - p = write_pair_number(p, ngtcp2_vec_lit(&name, "ssthresh"), - cstat->ssthresh); - } - - *p++ = '}'; - *p++ = ']'; - *p++ = ','; - - qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, - (size_t)(p - buf)); -} - -void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { - uint8_t buf[256]; - uint8_t *p = buf; - ngtcp2_vec name, value; - ngtcp2_pkt_hd hd; - - if (!qlog->write) { - return; - } - - *p++ = '['; - p = write_tstamp(p, qlog->last_ts - qlog->ts); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "recovery")); - *p++ = ','; - p = write_string(p, ngtcp2_vec_lit(&value, "packet_lost")); - *p++ = ','; - *p++ = '{'; - - hd.type = ent->hd.type; - hd.flags = ent->hd.flags; - - p = write_pair(p, ngtcp2_vec_lit(&name, "packet_type"), - qlog_pkt_type(&value, &hd)); - *p++ = ','; - p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "packet_number"), - (uint64_t)ent->hd.pkt_num); - *p++ = '}'; - *p++ = ']'; - *p++ = ','; - - qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, - (size_t)(p - buf)); -} diff --git a/deps/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/lib/ngtcp2_qlog.h deleted file mode 100644 index 0709f92a364..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_qlog.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_QLOG_H -#define NGTCP2_QLOG_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_pkt.h" -#include "ngtcp2_cc.h" -#include "ngtcp2_buf.h" -#include "ngtcp2_rtb.h" - -/* NGTCP2_QLOG_BUFLEN is the length of heap allocated buffer for - qlog. */ -#define NGTCP2_QLOG_BUFLEN 4096 - -typedef enum ngtcp2_qlog_side { - NGTCP2_QLOG_SIDE_LOCAL, - NGTCP2_QLOG_SIDE_REMOTE, -} ngtcp2_qlog_side; - -typedef struct ngtcp2_qlog { - /* write is a callback function to write qlog. */ - ngtcp2_qlog_write write; - /* ts is the initial timestamp */ - ngtcp2_tstamp ts; - /* last_ts is the timestamp observed last time. */ - ngtcp2_tstamp last_ts; - /* buf is a heap allocated buffer to write exclusively - packet_received and packet_sent. */ - ngtcp2_buf buf; - /* user_data is an opaque pointer which is passed to write - callback. */ - void *user_data; -} ngtcp2_qlog; - -/* - * ngtcp2_qlog_init initializes |qlog|. - */ -void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, - ngtcp2_tstamp ts, void *user_data); - -/* - * ngtcp2_qlog_start writes qlog preamble. - */ -void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server); - -/* - * ngtcp2_qlog_end writes closing part of qlog. - */ -void ngtcp2_qlog_end(ngtcp2_qlog *qlog); - -/* - * ngtcp2_qlog_write_frame writes |fr| to qlog->buf. - * ngtcp2_qlog_pkt_received_start or ngtcp2_qlog_pkt_sent_start must - * be called before calling this function. - */ -void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr); - -/* - * ngtcp2_qlog_pkt_received_start starts to write packet_received - * event. It initializes qlog->buf. It writes qlog to qlog->buf. - * ngtcp2_qlog_pkt_received_end will flush the content of qlog->buf to - * write callback. - */ -void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); - -/* - * ngtcp2_qlog_pkt_received_end ends packet_received event and sends - * the content of qlog->buf to qlog->write callback. - */ -void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, - size_t pktlen); - -/* - * ngtcp2_qlog_pkt_sent_start starts to write packet_sent event. It - * initializes qlog->buf. It writes qlog to qlog->buf. - * ngtcp2_qlog_pkt_sent_end will flush the content of qlog->buf to - * write callback. - */ -void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); - -/* - * ngtcp2_qlog_pkt_sent_end ends packet_sent event and sends the - * content of qlog->buf to qlog->write callback. - */ -void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, - size_t pktlen); - -/* - * ngtcp2_qlog_parameters_set_transport_params writes |params| to qlog - * as parameters_set event. |server| is nonzero if the local endpoint - * is server. If |local| is nonzero, it is "owner" field becomes - * "local", otherwise "remote". - */ -void ngtcp2_qlog_parameters_set_transport_params( - ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server, - ngtcp2_qlog_side side); - -/* - * ngtcp2_qlog_metrics_updated writes metrics_updated event of - * recovery category. - */ -void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, - const ngtcp2_conn_stat *cstat); - -/* - * ngtcp2_qlog_pkt_lost writes packet_lost event. - */ -void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent); - -/* connection_id_updated */ - -#endif /* NGTCP2_QLOG_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_range.c b/deps/ngtcp2/lib/ngtcp2_range.c deleted file mode 100644 index 9379496b7d4..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_range.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_range.h" -#include "ngtcp2_macro.h" - -void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end) { - r->begin = begin; - r->end = end; -} - -ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a, - const ngtcp2_range *b) { - ngtcp2_range r = {0, 0}; - uint64_t begin = ngtcp2_max(a->begin, b->begin); - uint64_t end = ngtcp2_min(a->end, b->end); - if (begin < end) { - ngtcp2_range_init(&r, begin, end); - } - return r; -} - -uint64_t ngtcp2_range_len(const ngtcp2_range *r) { return r->end - r->begin; } - -int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b) { - return a->begin == b->begin && a->end == b->end; -} - -void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right, - const ngtcp2_range *a, const ngtcp2_range *b) { - /* Assume that b is included in a */ - left->begin = a->begin; - left->end = b->begin; - right->begin = b->end; - right->end = a->end; -} - -int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b) { - return a->end <= b->end; -} diff --git a/deps/ngtcp2/lib/ngtcp2_range.h b/deps/ngtcp2/lib/ngtcp2_range.h deleted file mode 100644 index 96b9a54b28f..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_range.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_RANGE_H -#define NGTCP2_RANGE_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -/* - * ngtcp2_range represents half-closed range [begin, end). - */ -typedef struct { - uint64_t begin; - uint64_t end; -} ngtcp2_range; - -/* - * ngtcp2_range_init initializes |r| with the range [|begin|, |end|). - */ -void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end); - -/* - * ngtcp2_range_intersect returns the intersection of |a| and |b|. If - * they do not overlap, it returns empty range. - */ -ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a, - const ngtcp2_range *b); - -/* - * ngtcp2_range_len returns the length of |r|. - */ -uint64_t ngtcp2_range_len(const ngtcp2_range *r); - -/* - * ngtcp2_range_eq returns nonzero if |a| equals |b|, such that - * a->begin == b->begin, and a->end == b->end hold. - */ -int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b); - -/* - * ngtcp2_range_cut returns the left and right range after removing - * |b| from |a|. This function assumes that |a| completely includes - * |b|. In other words, a->begin <= b->begin and b->end <= a->end - * hold. - */ -void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right, - const ngtcp2_range *a, const ngtcp2_range *b); - -/* - * ngtcp2_range_not_after returns nonzero if the right edge of |a| - * does not go beyond of the right edge of |b|. - */ -int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b); - -#endif /* NGTCP2_RANGE_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rcvry.h b/deps/ngtcp2/lib/ngtcp2_rcvry.h deleted file mode 100644 index e392c34ebfe..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_rcvry.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_RCVRY_H -#define NGTCP2_RCVRY_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in - draft-ietf-quic-recovery-22. */ -#define NGTCP2_PKT_THRESHOLD 3 - -/* NGTCP2_GRANULARITY is kGranularity described in - draft-ietf-quic-recovery-17. */ -#define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS - -#endif /* NGTCP2_RCVRY_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/lib/ngtcp2_ringbuf.c deleted file mode 100644 index e4deab1ff76..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_ringbuf.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_ringbuf.h" - -#include <assert.h> -#ifdef WIN32 -# include <intrin.h> -#endif - -#include "ngtcp2_macro.h" - -#if defined(_MSC_VER) && defined(_M_ARM64) -unsigned int __popcnt(unsigned int x) { - unsigned int c = 0; - for (; x; ++c) { - x &= x - 1; - } - return c; -} -#endif - -int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, - const ngtcp2_mem *mem) { -#ifdef WIN32 - assert(1 == __popcnt((unsigned int)nmemb)); -#else - assert(1 == __builtin_popcount((unsigned int)nmemb)); -#endif - - rb->buf = ngtcp2_mem_malloc(mem, nmemb * size); - if (rb->buf == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rb->mem = mem; - rb->nmemb = nmemb; - rb->size = size; - rb->first = 0; - rb->len = 0; - - return 0; -} - -void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) { - if (rb == NULL) { - return; - } - - ngtcp2_mem_free(rb->mem, rb->buf); -} - -void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb) { - rb->first = (rb->first - 1) & (rb->nmemb - 1); - rb->len = ngtcp2_min(rb->nmemb, rb->len + 1); - - return (void *)&rb->buf[rb->first * rb->size]; -} - -void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb) { - size_t offset = (rb->first + rb->len) & (rb->nmemb - 1); - - if (rb->len == rb->nmemb) { - rb->first = (rb->first + 1) & (rb->nmemb - 1); - } else { - ++rb->len; - } - - return (void *)&rb->buf[offset * rb->size]; -} - -void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb) { - rb->first = (rb->first + 1) & (rb->nmemb - 1); - --rb->len; -} - -void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb) { - assert(rb->len); - --rb->len; -} - -void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len) { - assert(len <= rb->nmemb); - rb->len = len; -} - -void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset) { - assert(offset < rb->len); - offset = (rb->first + offset) & (rb->nmemb - 1); - return &rb->buf[offset * rb->size]; -} - -int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb) { return rb->len == rb->nmemb; } diff --git a/deps/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/lib/ngtcp2_ringbuf.h deleted file mode 100644 index 6d546495f2a..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_ringbuf.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_RINGBUF_H -#define NGTCP2_RINGBUF_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" - -typedef struct { - /* buf points to the underlying buffer. */ - uint8_t *buf; - const ngtcp2_mem *mem; - /* nmemb is the number of elements that can be stored in this ring - buffer. */ - size_t nmemb; - /* size is the size of each element. */ - size_t size; - /* first is the offset to the first element. */ - size_t first; - /* len is the number of elements actually stored. */ - size_t len; -} ngtcp2_ringbuf; - -/* - * ngtcp2_ringbuf_init initializes |rb|. |nmemb| is the number of - * elements that can be stored in this buffer. |size| is the size of - * each element. |size| must be power of 2. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, - const ngtcp2_mem *mem); - -/* - * ngtcp2_ringbuf_free frees resources allocated for |rb|. This - * function does not free the memory pointed by |rb|. - */ -void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb); - -/* ngtcp2_ringbuf_push_front moves the offset to the first element in - the buffer backward, and returns the pointer to the element. - Caller can store data to the buffer pointed by the returned - pointer. If this action exceeds the capacity of the ring buffer, - the last element is silently overwritten, and rb->len remains - unchanged. */ -void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb); - -/* ngtcp2_ringbuf_push_back moves the offset to the last element in - the buffer forward, and returns the pointer to the element. Caller - can store data to the buffer pointed by the returned pointer. If - this action exceeds the capacity of the ring buffer, the first - element is silently overwritten, and rb->len remains unchanged. */ -void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb); - -/* - * ngtcp2_ringbuf_pop_front removes first element in |rb|. - */ -void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb); - -/* - * ngtcp2_ringbuf_pop_back removes the last element in |rb|. - */ -void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb); - -/* ngtcp2_ringbuf_resize changes the number of elements stored. This - does not change the capacity of the underlying buffer. */ -void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len); - -/* ngtcp2_ringbuf_get returns the pointer to the element at - |offset|. */ -void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset); - -/* ngtcp2_ringbuf_len returns the number of elements stored. */ -#define ngtcp2_ringbuf_len(RB) ((RB)->len) - -/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ -int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); - -#endif /* NGTCP2_RINGBUF_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/lib/ngtcp2_rob.c deleted file mode 100644 index 499c07ec6be..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_rob.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_rob.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_macro.h" - -int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, - const ngtcp2_mem *mem) { - *pg = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_gap)); - if (*pg == NULL) { - return NGTCP2_ERR_NOMEM; - } - - (*pg)->range.begin = begin; - (*pg)->range.end = end; - - return 0; -} - -void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, g); -} - -int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, - const ngtcp2_mem *mem) { - *pd = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_data) + chunk); - if (*pd == NULL) { - return NGTCP2_ERR_NOMEM; - } - - (*pd)->range.begin = offset; - (*pd)->range.end = offset + chunk; - (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data); - (*pd)->end = (*pd)->begin + chunk; - - return 0; -} - -void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, d); -} - -int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { - int rv; - ngtcp2_rob_gap *g; - - rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_gapksl_ksl_init; - } - - rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); - if (rv != 0) { - goto fail_rob_gap_new; - } - - rv = ngtcp2_ksl_insert(&rob->gapksl, NULL, &g->range, g); - if (rv != 0) { - goto fail_gapksl_ksl_insert; - } - - rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_dataksl_ksl_init; - } - - rob->chunk = chunk; - rob->mem = mem; - - return 0; - -fail_dataksl_ksl_init: -fail_gapksl_ksl_insert: - ngtcp2_rob_gap_del(g, mem); -fail_rob_gap_new: - ngtcp2_ksl_free(&rob->gapksl); -fail_gapksl_ksl_init: - return rv; -} - -void ngtcp2_rob_free(ngtcp2_rob *rob) { - ngtcp2_ksl_it it; - - if (rob == NULL) { - return; - } - - for (it = ngtcp2_ksl_begin(&rob->dataksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - ngtcp2_rob_data_del(ngtcp2_ksl_it_get(&it), rob->mem); - } - - for (it = ngtcp2_ksl_begin(&rob->gapksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - ngtcp2_rob_gap_del(ngtcp2_ksl_it_get(&it), rob->mem); - } - - ngtcp2_ksl_free(&rob->dataksl); - ngtcp2_ksl_free(&rob->gapksl); -} - -static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, - size_t len) { - size_t n; - int rv; - ngtcp2_rob_data *d; - ngtcp2_range range = {offset, offset + len}; - ngtcp2_ksl_it it; - - for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl, &range, - ngtcp2_ksl_range_exclusive_compar); - len; ngtcp2_ksl_it_next(&it)) { - if (ngtcp2_ksl_it_end(&it)) { - d = NULL; - } else { - d = ngtcp2_ksl_it_get(&it); - } - - if (d == NULL || offset < d->range.begin) { - rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk, - rob->chunk, rob->mem); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_ksl_insert(&rob->dataksl, &it, &d->range, d); - if (rv != 0) { - ngtcp2_rob_data_del(d, rob->mem); - return rv; - } - } - - n = (size_t)ngtcp2_min((uint64_t)len, d->range.begin + rob->chunk - offset); - memcpy(d->begin + (offset - d->range.begin), data, n); - offset += n; - data += n; - len -= n; - } - - return 0; -} - -int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, - size_t datalen) { - int rv; - ngtcp2_rob_gap *g; - ngtcp2_range m, l, r, q = {offset, offset + datalen}; - ngtcp2_ksl_it it; - - it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, &q, - ngtcp2_ksl_range_exclusive_compar); - - for (; !ngtcp2_ksl_it_end(&it);) { - g = ngtcp2_ksl_it_get(&it); - - m = ngtcp2_range_intersect(&q, &g->range); - if (!ngtcp2_range_len(&m)) { - break; - } - if (ngtcp2_range_eq(&g->range, &m)) { - ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); - ngtcp2_rob_gap_del(g, rob->mem); - rv = rob_write_data(rob, m.begin, data + (m.begin - offset), - (size_t)ngtcp2_range_len(&m)); - if (rv != 0) { - return rv; - } - - continue; - } - ngtcp2_range_cut(&l, &r, &g->range, &m); - if (ngtcp2_range_len(&l)) { - ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &l); - g->range = l; - - if (ngtcp2_range_len(&r)) { - ngtcp2_rob_gap *ng; - rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem); - if (rv != 0) { - return rv; - } - rv = ngtcp2_ksl_insert(&rob->gapksl, &it, &ng->range, ng); - if (rv != 0) { - ngtcp2_rob_gap_del(ng, rob->mem); - return rv; - } - } - } else if (ngtcp2_range_len(&r)) { - ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r); - g->range = r; - } - rv = rob_write_data(rob, m.begin, data + (m.begin - offset), - (size_t)ngtcp2_range_len(&m)); - if (rv != 0) { - return rv; - } - ngtcp2_ksl_it_next(&it); - } - return 0; -} - -int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { - ngtcp2_rob_gap *g; - ngtcp2_rob_data *d; - ngtcp2_ksl_it it; - - it = ngtcp2_ksl_begin(&rob->gapksl); - - for (; !ngtcp2_ksl_it_end(&it);) { - g = ngtcp2_ksl_it_get(&it); - if (offset <= g->range.begin) { - break; - } - if (offset < g->range.end) { - ngtcp2_range r = {offset, g->range.end}; - ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r); - g->range.begin = offset; - break; - } - ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); - ngtcp2_rob_gap_del(g, rob->mem); - } - - it = ngtcp2_ksl_begin(&rob->dataksl); - - for (; !ngtcp2_ksl_it_end(&it);) { - d = ngtcp2_ksl_it_get(&it); - if (offset < d->range.begin + rob->chunk) { - return 0; - } - ngtcp2_ksl_remove(&rob->dataksl, &it, &d->range); - ngtcp2_rob_data_del(d, rob->mem); - } - - return 0; -} - -size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest, - uint64_t offset) { - ngtcp2_rob_gap *g; - ngtcp2_rob_data *d; - ngtcp2_ksl_it it; - - it = ngtcp2_ksl_begin(&rob->gapksl); - if (ngtcp2_ksl_it_end(&it)) { - return 0; - } - - g = ngtcp2_ksl_it_get(&it); - - if (g->range.begin <= offset) { - return 0; - } - - it = ngtcp2_ksl_begin(&rob->dataksl); - d = ngtcp2_ksl_it_get(&it); - - assert(d); - assert(d->range.begin <= offset); - assert(offset < d->range.begin + rob->chunk); - - *pdest = d->begin + (offset - d->range.begin); - - return (size_t)(ngtcp2_min(g->range.begin, d->range.begin + rob->chunk) - - offset); -} - -void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) { - ngtcp2_ksl_it it; - ngtcp2_rob_data *d; - - it = ngtcp2_ksl_begin(&rob->dataksl); - d = ngtcp2_ksl_it_get(&it); - - assert(d); - - if (offset + len < d->range.begin + rob->chunk) { - return; - } - - ngtcp2_ksl_remove(&rob->dataksl, NULL, &d->range); - ngtcp2_rob_data_del(d, rob->mem); -} - -uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rob->gapksl); - ngtcp2_rob_gap *g; - - if (ngtcp2_ksl_it_end(&it)) { - return UINT64_MAX; - } - - g = ngtcp2_ksl_it_get(&it); - - return g->range.begin; -} - -int ngtcp2_rob_data_buffered(ngtcp2_rob *rob) { - return ngtcp2_ksl_len(&rob->dataksl) != 0; -} diff --git a/deps/ngtcp2/lib/ngtcp2_rob.h b/deps/ngtcp2/lib/ngtcp2_rob.h deleted file mode 100644 index c6a039ce408..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_rob.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_ROB_H -#define NGTCP2_ROB_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_mem.h" -#include "ngtcp2_range.h" -#include "ngtcp2_ksl.h" - -struct ngtcp2_rob_gap; -typedef struct ngtcp2_rob_gap ngtcp2_rob_gap; - -/* - * ngtcp2_rob_gap represents the gap, which is the range of stream - * data that is not received yet. - */ -struct ngtcp2_rob_gap { - /* range is the range of this gap. */ - ngtcp2_range range; -}; - -/* - * ngtcp2_rob_gap_new allocates new ngtcp2_rob_gap object, and assigns - * its pointer to |*pg|. The caller should call ngtcp2_rob_gap_del to - * delete it when it is no longer used. The range of the gap is - * [begin, end). |mem| is custom memory allocator to allocate memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, - const ngtcp2_mem *mem); - -/* - * ngtcp2_rob_gap_del deallocates |g|. It deallocates the memory - * pointed by |g| it self. |mem| is custom memory allocator to - * deallocate memory. - */ -void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem); - -struct ngtcp2_rob_data; -typedef struct ngtcp2_rob_data ngtcp2_rob_data; - -/* - * ngtcp2_rob_data holds the buffered stream data. - */ -struct ngtcp2_rob_data { - /* range is the range of this gap. */ - ngtcp2_range range; - /* begin points to the buffer. */ - uint8_t *begin; - /* end points to the one beyond of the last byte of the buffer */ - uint8_t *end; -}; - -/* - * ngtcp2_rob_data_new allocates new ngtcp2_rob_data object, and - * assigns its pointer to |*pd|. The caller should call - * ngtcp2_rob_data_del to delete it when it is no longer used. - * |offset| is the stream offset of the first byte of this data. - * |chunk| is the size of the buffer. |offset| must be multiple of - * |chunk|. |mem| is custom memory allocator to allocate memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, - const ngtcp2_mem *mem); - -/* - * ngtcp2_rob_data_del deallocates |d|. It deallocates the memory - * pointed by |d| itself. |mem| is custom memory allocator to - * deallocate memory. - */ -void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem); - -/* - * ngtcp2_rob is the reorder buffer which reassembles stream data - * received in out of order. - */ -typedef struct { - /* gapksl maintains the range of offset which is not received - yet. Initially, its range is [0, UINT64_MAX). */ - ngtcp2_ksl gapksl; - /* dataksl maintains the list of buffers which store received data - ordered by stream offset. */ - ngtcp2_ksl dataksl; - /* mem is custom memory allocator */ - const ngtcp2_mem *mem; - /* chunk is the size of each buffer in data field */ - size_t chunk; -} ngtcp2_rob; - -/* - * ngtcp2_rob_init initializes |rob|. |chunk| is the size of buffer - * per chunk. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem); - -/* - * ngtcp2_rob_free frees resources allocated for |rob|. - */ -void ngtcp2_rob_free(ngtcp2_rob *rob); - -/* - * ngtcp2_rob_push adds new data of length |datalen| at the stream - * offset |offset|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, - size_t datalen); - -/* - * ngtcp2_rob_remove_prefix removes gap up to |offset|, exclusive. It - * also removes data buffer if it is completely included in |offset|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset); - -/* - * ngtcp2_rob_data_at stores the pointer to the buffer of stream - * offset |offset| to |*pdest| if it is available, and returns the - * valid length of available data. If no data is available, it - * returns 0. - */ -size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest, - uint64_t offset); - -/* - * ngtcp2_rob_pop clears data at stream offset |offset| of length - * |len|. - * - * |offset| must be the offset given in ngtcp2_rob_data_at. |len| - * must be the return value of ngtcp2_rob_data_at when |offset| is - * passed. - * - * Caller should call this function from offset 0 in non-decreasing - * order. - */ -void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len); - -/* - * ngtcp2_rob_first_gap_offset returns the offset to the first gap. - * If there is no gap, it returns UINT64_MAX. - */ -uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob); - -/* - * ngtcp2_rob_data_buffered returns nonzero if any data is buffered. - */ -int ngtcp2_rob_data_buffered(ngtcp2_rob *rob); - -#endif /* NGTCP2_ROB_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/lib/ngtcp2_rst.c deleted file mode 100644 index e546fdf85c6..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_rst.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_rst.h" -#include "ngtcp2_rtb.h" -#include "ngtcp2_cc.h" -#include "ngtcp2_macro.h" - -void ngtcp2_rs_init(ngtcp2_rs *rs) { - rs->interval = UINT64_MAX; - rs->delivered = 0; - rs->prior_delivered = 0; - rs->prior_ts = 0; - rs->send_elapsed = 0; - rs->ack_elapsed = 0; - rs->is_app_limited = 0; -} - -void ngtcp2_rst_init(ngtcp2_rst *rst) { - ngtcp2_rs_init(&rst->rs); - rst->delivered = 0; - rst->delivered_ts = 0; - rst->first_sent_ts = 0; - rst->app_limited = 0; -} - -void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, - const ngtcp2_conn_stat *cstat) { - if (cstat->bytes_in_flight == 0) { - rst->first_sent_ts = rst->delivered_ts = ent->ts; - } - ent->rst.first_sent_ts = rst->first_sent_ts; - ent->rst.delivered_ts = rst->delivered_ts; - ent->rst.delivered = rst->delivered; - ent->rst.is_app_limited = rst->app_limited != 0; -} - -int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { - ngtcp2_rs *rs = &rst->rs; - - if (rst->app_limited && rst->delivered > rst->app_limited) { - rst->app_limited = 0; - } - - if (rs->prior_ts == 0) { - return 0; - } - - rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed); - - rs->delivered = rst->delivered - rs->prior_delivered; - - if (rs->interval < cstat->min_rtt) { - rs->interval = UINT64_MAX; - return 0; - } - - if (rs->interval) { - cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval; - } - - return 0; -} - -void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, - ngtcp2_tstamp ts) { - ngtcp2_rs *rs = &rst->rs; - - rst->delivered += ent->pktlen; - rst->delivered_ts = ts; - - if (ent->rst.delivered > rs->prior_delivered) { - 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; - rst->first_sent_ts = ent->ts; - } -} - -void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { - (void)rst; - (void)cstat; - /* TODO Not implemented */ -} diff --git a/deps/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/lib/ngtcp2_rst.h deleted file mode 100644 index f68a1f9b22d..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_rst.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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_RST_H -#define NGTCP2_RST_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -struct ngtcp2_rtb_entry; -typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; - -/** - * @struct - * - * ngtcp2_rs contains connection state for delivery rate estimation. - */ -typedef struct ngtcp2_rs { - ngtcp2_duration interval; - uint64_t delivered; - uint64_t prior_delivered; - ngtcp2_tstamp prior_ts; - ngtcp2_duration send_elapsed; - ngtcp2_duration ack_elapsed; - int is_app_limited; -} ngtcp2_rs; - -void ngtcp2_rs_init(ngtcp2_rs *rs); - -/* - * ngtcp2_rst implements delivery rate estimation described in - * https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00 - */ -typedef struct ngtcp2_rst { - ngtcp2_rs rs; - uint64_t delivered; - ngtcp2_tstamp delivered_ts; - ngtcp2_tstamp first_sent_ts; - uint64_t app_limited; -} ngtcp2_rst; - -void ngtcp2_rst_init(ngtcp2_rst *rst); - -void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, - const ngtcp2_conn_stat *cstat); -int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); -void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, - ngtcp2_tstamp ts); -void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); - -#endif /* NGTCP2_RST_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/lib/ngtcp2_rtb.c deleted file mode 100644 index d58a6b2ea1a..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_rtb.c +++ /dev/null @@ -1,1172 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_rtb.h" - -#include <assert.h> -#include <string.h> - -#include "ngtcp2_macro.h" -#include "ngtcp2_conn.h" -#include "ngtcp2_log.h" -#include "ngtcp2_vec.h" -#include "ngtcp2_cc.h" -#include "ngtcp2_rcvry.h" -#include "ngtcp2_rst.h" - -int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) { - *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain)); - if (*pfrc == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_frame_chain_init(*pfrc); - - return 0; -} - -int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, - const ngtcp2_mem *mem) { - *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen); - if (*pfrc == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_frame_chain_init(*pfrc); - - return 0; -} - -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { - size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); - - if (datacnt > 0 && need > avail) { - return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); - } - - return ngtcp2_frame_chain_new(pfrc, mem); -} - -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { - size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); - - if (datacnt > 0 && need > avail) { - return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); - } - - return ngtcp2_frame_chain_new(pfrc, mem); -} - -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem) { - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token); - int rv; - uint8_t *p; - ngtcp2_frame *fr; - - if (token->len > avail) { - rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem); - } else { - rv = ngtcp2_frame_chain_new(pfrc, mem); - } - if (rv != 0) { - return rv; - } - - fr = &(*pfrc)->fr; - fr->type = NGTCP2_FRAME_NEW_TOKEN; - - p = (uint8_t *)(*pfrc) + sizeof(ngtcp2_new_token); - memcpy(p, token->base, token->len); - - ngtcp2_vec_init(&fr->new_token.token, p, token->len); - - return 0; -} - -void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { - ngtcp2_frame_chain_binder *binder; - - if (frc == NULL) { - return; - } - - binder = frc->binder; - if (binder && --binder->refcount == 0) { - ngtcp2_mem_free(mem, binder); - } - - ngtcp2_mem_free(mem, frc); -} - -void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { - frc->next = NULL; - frc->binder = NULL; -} - -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem) { - ngtcp2_frame_chain *next; - - for (; frc;) { - next = frc->next; - ngtcp2_frame_chain_del(frc, mem); - frc = next; - } -} - -int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder, - const ngtcp2_mem *mem) { - *pbinder = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_frame_chain_binder)); - if (*pbinder == NULL) { - return NGTCP2_ERR_NOMEM; - } - - return 0; -} - -int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, - const ngtcp2_mem *mem) { - ngtcp2_frame_chain_binder *binder; - int rv; - - assert(b->binder == NULL); - - if (a->binder == NULL) { - rv = ngtcp2_frame_chain_binder_new(&binder, mem); - if (rv != 0) { - return rv; - } - - a->binder = binder; - ++a->binder->refcount; - } - - b->binder = a->binder; - ++b->binder->refcount; - - return 0; -} - -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) { - (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry)); - if (*pent == NULL) { - return NGTCP2_ERR_NOMEM; - } - - (*pent)->hd.pkt_num = hd->pkt_num; - (*pent)->hd.type = hd->type; - (*pent)->hd.flags = hd->flags; - (*pent)->frc = frc; - (*pent)->ts = ts; - (*pent)->lost_ts = UINT64_MAX; - (*pent)->pktlen = pktlen; - (*pent)->flags = flags; - (*pent)->next = NULL; - - return 0; -} - -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) { - if (ent == NULL) { - return; - } - - ngtcp2_frame_chain_list_del(ent->frc, mem); - - ngtcp2_mem_free(mem, ent); -} - -static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { - return *(int64_t *)lhs > *(int64_t *)rhs; -} - -void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, - ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, - ngtcp2_log *log, ngtcp2_qlog *qlog, - const ngtcp2_mem *mem) { - ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem); - rtb->crypto = crypto; - rtb->rst = rst; - rtb->cc = cc; - rtb->log = log; - rtb->qlog = qlog; - rtb->mem = mem; - rtb->largest_acked_tx_pkt_num = -1; - rtb->num_ack_eliciting = 0; - rtb->num_retransmittable = 0; - rtb->probe_pkt_left = 0; - rtb->pktns_id = pktns_id; - rtb->cc_pkt_num = 0; - rtb->cc_bytes_in_flight = 0; - rtb->persistent_congestion_start_ts = UINT64_MAX; - rtb->num_lost_pkts = 0; -} - -void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { - ngtcp2_ksl_it it; - - if (rtb == NULL) { - return; - } - - it = ngtcp2_ksl_begin(&rtb->ents); - - for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem); - } - - ngtcp2_ksl_free(&rtb->ents); -} - -static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat) { - ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat); - - assert(rtb->cc_pkt_num <= ent->hd.pkt_num); - - cstat->bytes_in_flight += ent->pktlen; - rtb->cc_bytes_in_flight += ent->pktlen; - - ngtcp2_rst_update_app_limited(rtb->rst, cstat); - - if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { - ++rtb->num_ack_eliciting; - } - if (ent->flags & NGTCP2_RTB_FLAG_RETRANSMITTABLE) { - ++rtb->num_retransmittable; - } -} - -static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat) { - if (ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED) { - return; - } - - if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { - assert(rtb->num_ack_eliciting); - --rtb->num_ack_eliciting; - } - - if ((ent->flags & NGTCP2_RTB_FLAG_RETRANSMITTABLE) && - !(ent->flags & NGTCP2_RTB_FLAG_PTO_RECLAIMED)) { - assert(rtb->num_retransmittable); - --rtb->num_retransmittable; - } - - if (rtb->cc_pkt_num <= ent->hd.pkt_num) { - assert(cstat->bytes_in_flight >= ent->pktlen); - cstat->bytes_in_flight -= ent->pktlen; - - assert(rtb->cc_bytes_in_flight >= ent->pktlen); - rtb->cc_bytes_in_flight -= ent->pktlen; - } -} - -/* - * rtb_reclaim_frame queues unacknowledged frames included in |ent| - * for retransmission. The re-queued frames are not deleted from - * |ent|. It returns the number of frames queued. - */ -static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, - ngtcp2_rtb_entry *ent) { - ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq; - ngtcp2_frame *fr; - ngtcp2_strm *strm; - ngtcp2_range gap, range; - size_t num_reclaimed = 0; - int rv; - - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ - /* TODO Reconsider the order of pfrc */ - for (frc = ent->frc; frc; frc = frc->next) { - fr = &frc->fr; - /* Check that a late ACK acknowledged this frame. */ - if (frc->binder && - (frc->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { - continue; - } - switch (frc->fr.type) { - case NGTCP2_FRAME_STREAM: - strm = ngtcp2_conn_find_stream(conn, fr->stream.stream_id); - if (strm == NULL) { - continue; - } - - gap = ngtcp2_strm_get_unacked_range_after(strm, fr->stream.offset); - - range.begin = fr->stream.offset; - range.end = fr->stream.offset + - ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); - range = ngtcp2_range_intersect(&range, &gap); - if (ngtcp2_range_len(&range) == 0 && - (!fr->stream.fin || (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED))) { - continue; - } - - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt, - rtb->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr = *fr; - ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data, - fr->stream.datacnt); - - rv = ngtcp2_strm_streamfrq_push(strm, nfrc); - if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); - return rv; - } - if (!ngtcp2_strm_is_tx_queued(strm)) { - strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn); - rv = ngtcp2_conn_tx_strmq_push(conn, strm); - if (rv != 0) { - return rv; - } - } - - ++num_reclaimed; - - continue; - case NGTCP2_FRAME_CRYPTO: - /* Don't resend CRYPTO frame if the whole region it contains has - been acknowledged */ - gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->crypto.offset); - - range.begin = fr->crypto.offset; - range.end = fr->crypto.offset + - ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt); - range = ngtcp2_range_intersect(&range, &gap); - if (ngtcp2_range_len(&range) == 0) { - continue; - } - - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt, - rtb->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr = *fr; - ngtcp2_vec_copy(nfrc->fr.crypto.data, fr->crypto.data, - fr->crypto.datacnt); - - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, - &nfrc->fr.crypto.offset, nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - return rv; - } - - ++num_reclaimed; - - continue; - case NGTCP2_FRAME_NEW_TOKEN: - rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token, - rtb->mem); - if (rv != 0) { - return rv; - } - - rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem); - if (rv != 0) { - return rv; - } - - break; - default: - rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr = *fr; - - rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem); - if (rv != 0) { - return rv; - } - - break; - } - - ++num_reclaimed; - - nfrc->next = *pfrc; - *pfrc = nfrc; - pfrc = &nfrc->next; - } - - return (ngtcp2_ssize)num_reclaimed; -} - -static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, - ngtcp2_rtb_entry *ent, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_tstamp ts) { - int rv; - ngtcp2_ssize reclaimed; - - 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_FLAG_PROBE)) { - if (ent->flags & NGTCP2_RTB_FLAG_PTO_RECLAIMED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " has already been reclaimed on PTO", - ent->hd.pkt_num); - assert(!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); - - ent->flags |= NGTCP2_RTB_FLAG_LOST_RETRANSMITTED; - ent->lost_ts = ts; - - ++rtb->num_lost_pkts; - - ngtcp2_ksl_it_next(it); - - return 0; - } - - if (ent->frc) { - assert(!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); - - reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); - if (reclaimed < 0) { - return (int)reclaimed; - } - - if (reclaimed) { - ent->flags |= NGTCP2_RTB_FLAG_LOST_RETRANSMITTED; - ent->lost_ts = ts; - - ++rtb->num_lost_pkts; - - ngtcp2_ksl_it_next(it); - - return 0; - } - } - } else { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 - " is a probe packet, no retransmission is necessary", - ent->hd.pkt_num); - } - - rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); - assert(0 == rv); - - ngtcp2_rtb_entry_del(ent, rtb->mem); - - return 0; -} - -int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat) { - int rv; - - rv = ngtcp2_ksl_insert(&rtb->ents, NULL, &ent->hd.pkt_num, ent); - if (rv != 0) { - return rv; - } - - rtb_on_add(rtb, ent, cstat); - - return 0; -} - -ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb) { - return ngtcp2_ksl_begin(&rtb->ents); -} - -static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, - ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat) { - int rv; - rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); - assert(0 == rv); - rtb_on_remove(rtb, ent, cstat); - - assert(ent->next == NULL); - - ngtcp2_list_insert(ent, pent); -} - -static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn *conn) { - ngtcp2_frame_chain *frc; - uint64_t prev_stream_offset, stream_offset; - ngtcp2_strm *strm; - int rv; - uint64_t datalen; - ngtcp2_strm *crypto = rtb->crypto; - ngtcp2_crypto_level crypto_level; - - for (frc = ent->frc; frc; frc = frc->next) { - if (frc->binder) { - frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK; - } - - switch (frc->fr.type) { - case NGTCP2_FRAME_STREAM: - strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id); - if (strm == NULL) { - break; - } - - if (frc->fr.stream.fin) { - strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; - } - - prev_stream_offset = ngtcp2_strm_get_acked_offset(strm); - rv = ngtcp2_strm_ack_data( - strm, frc->fr.stream.offset, - ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); - if (rv != 0) { - return rv; - } - - if (conn->callbacks.acked_stream_data_offset) { - stream_offset = ngtcp2_strm_get_acked_offset(strm); - datalen = stream_offset - prev_stream_offset; - if (datalen == 0 && !frc->fr.stream.fin) { - break; - } - - rv = conn->callbacks.acked_stream_data_offset( - conn, strm->stream_id, prev_stream_offset, datalen, conn->user_data, - strm->stream_user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - - rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); - if (rv != 0) { - return rv; - } - break; - case NGTCP2_FRAME_CRYPTO: - prev_stream_offset = ngtcp2_strm_get_acked_offset(crypto); - rv = ngtcp2_strm_ack_data( - crypto, frc->fr.crypto.offset, - ngtcp2_vec_len(frc->fr.crypto.data, frc->fr.crypto.datacnt)); - if (rv != 0) { - return rv; - } - - if (conn->callbacks.acked_crypto_offset) { - stream_offset = ngtcp2_strm_get_acked_offset(crypto); - datalen = stream_offset - prev_stream_offset; - if (datalen == 0) { - break; - } - - switch (rtb->pktns_id) { - case NGTCP2_PKTNS_ID_INITIAL: - crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; - break; - case NGTCP2_PKTNS_ID_HANDSHAKE: - crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; - break; - case NGTCP2_PKTNS_ID_APP: - crypto_level = NGTCP2_CRYPTO_LEVEL_APP; - break; - default: - assert(0); - } - - rv = conn->callbacks.acked_crypto_offset( - conn, crypto_level, prev_stream_offset, datalen, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - break; - case NGTCP2_FRAME_RESET_STREAM: - strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id); - if (strm == NULL) { - break; - } - strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED; - rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); - if (rv != 0) { - return rv; - } - break; - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - assert(conn->dcid.num_retire_queued); - --conn->dcid.num_retire_queued; - break; - } - } - return 0; -} - -static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { - ngtcp2_cc *cc = rtb->cc; - ngtcp2_cc_pkt pkt; - - ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts); - - cc->on_pkt_acked(cc, cstat, - ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, - rtb->pktns_id, ent->ts), - ts); - - if (!(ent->flags & NGTCP2_RTB_FLAG_PROBE) && - (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { - cstat->pto_count = 0; - } -} - -ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, - ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, - ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { - ngtcp2_rtb_entry *ent; - int64_t largest_ack = fr->largest_ack, min_ack; - size_t i; - int rv; - ngtcp2_ksl_it it; - ngtcp2_ssize num_acked = 0; - ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX; - int64_t pkt_num; - ngtcp2_cc *cc = rtb->cc; - ngtcp2_rtb_entry *acked_ent = NULL; - int ack_eliciting_pkt_acked = 0; - - if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && - largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; - conn->crypto.key_update.confirmed_ts = ts; - - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); - } - - rtb->largest_acked_tx_pkt_num = - ngtcp2_max(rtb->largest_acked_tx_pkt_num, largest_ack); - - /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ - it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); - if (ngtcp2_ksl_it_end(&it)) { - return 0; - } - - min_ack = largest_ack - (int64_t)fr->first_ack_blklen; - - for (; !ngtcp2_ksl_it_end(&it);) { - pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it); - - assert(pkt_num <= largest_ack); - - if (pkt_num < min_ack) { - break; - } - - ent = ngtcp2_ksl_it_get(&it); - - if (largest_ack == pkt_num) { - largest_pkt_sent_ts = ent->ts; - } - - if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { - ack_eliciting_pkt_acked = 1; - } - - rtb_remove(rtb, &it, &acked_ent, ent, cstat); - ++num_acked; - } - - for (i = 0; i < fr->num_blks;) { - largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2; - min_ack = largest_ack - (int64_t)fr->blks[i].blklen; - - it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); - if (ngtcp2_ksl_it_end(&it)) { - break; - } - - for (; !ngtcp2_ksl_it_end(&it);) { - pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it); - if (pkt_num < min_ack) { - break; - } - ent = ngtcp2_ksl_it_get(&it); - - if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { - ack_eliciting_pkt_acked = 1; - } - - rtb_remove(rtb, &it, &acked_ent, ent, cstat); - ++num_acked; - } - - ++i; - } - - if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) { - ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, - fr->ack_delay_unscaled); - if (cc->new_rtt_sample) { - cc->new_rtt_sample(cc, cstat, ts); - } - } - - ngtcp2_rst_on_ack_recv(rtb->rst, cstat); - - if (conn) { - for (ent = acked_ent; ent; ent = acked_ent) { - rv = rtb_process_acked_pkt(rtb, ent, conn); - if (rv != 0) { - goto fail; - } - - rtb_on_pkt_acked(rtb, ent, cstat, ts); - acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); - } - } else { - /* For unit tests */ - for (ent = acked_ent; ent; ent = acked_ent) { - rtb_on_pkt_acked(rtb, ent, cstat, ts); - acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); - } - } - - cc->on_ack_recv(cc, cstat, ts); - - return num_acked; - -fail: - for (ent = acked_ent; ent; ent = acked_ent) { - acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); - } - - return rv; -} - -static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat, - const ngtcp2_rtb_entry *ent, uint64_t loss_delay, - ngtcp2_tstamp lost_send_time, uint64_t pkt_thres) { - ngtcp2_tstamp loss_time; - - if (ent->ts <= lost_send_time || - rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) { - return 1; - } - - loss_time = cstat->loss_time[rtb->pktns_id]; - - if (loss_time == UINT64_MAX) { - loss_time = ent->ts + loss_delay; - } else { - loss_time = ngtcp2_min(loss_time, ent->ts + loss_delay); - } - - cstat->loss_time[rtb->pktns_id] = loss_time; - - return 0; -} - -/* - * rtb_compute_pkt_loss_delay computes loss delay. - */ -static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_conn_stat *cstat) { - /* 9/8 is kTimeThreshold */ - ngtcp2_duration loss_delay = - ngtcp2_max(cstat->latest_rtt, cstat->smoothed_rtt) * 9 / 8; - return ngtcp2_max(loss_delay, NGTCP2_GRANULARITY); -} - -int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, - ngtcp2_duration pto, ngtcp2_tstamp ts) { - ngtcp2_rtb_entry *ent; - ngtcp2_duration loss_delay; - ngtcp2_tstamp lost_send_time; - ngtcp2_ksl_it it; - ngtcp2_tstamp latest_ts, oldest_ts; - int64_t last_lost_pkt_num; - ngtcp2_duration loss_window, congestion_period; - ngtcp2_cc *cc = rtb->cc; - int rv; - uint64_t pkt_thres = - rtb->cc_bytes_in_flight / cstat->max_udp_payload_size / 2; - - pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD); - cstat->loss_time[rtb->pktns_id] = UINT64_MAX; - loss_delay = compute_pkt_loss_delay(cstat); - lost_send_time = ts - loss_delay; - - it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num); - for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ent = ngtcp2_ksl_it_get(&it); - - if (ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED) { - break; - } - - if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, lost_send_time, pkt_thres)) { - /* All entries from ent are considered to be lost. */ - latest_ts = oldest_ts = ent->ts; - last_lost_pkt_num = ent->hd.pkt_num; - - congestion_period = pto * NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; - - for (; !ngtcp2_ksl_it_end(&it);) { - ent = ngtcp2_ksl_it_get(&it); - - if (last_lost_pkt_num == ent->hd.pkt_num + 1 && - ent->ts >= rtb->persistent_congestion_start_ts) { - last_lost_pkt_num = ent->hd.pkt_num; - oldest_ts = ent->ts; - } else { - last_lost_pkt_num = -1; - } - - if ((ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED)) { - if (rtb->pktns_id != NGTCP2_PKTNS_ID_APP || last_lost_pkt_num == -1 || - latest_ts - oldest_ts >= congestion_period) { - break; - } - ngtcp2_ksl_it_next(&it); - continue; - } - - rtb_on_remove(rtb, ent, cstat); - rv = rtb_on_pkt_lost(rtb, &it, ent, conn, pktns, ts); - if (rv != 0) { - return rv; - } - } - - cc->congestion_event(cc, cstat, latest_ts, ts); - - loss_window = latest_ts - oldest_ts; - /* Persistent congestion situation is only evaluated for app - * packet number space and for the packets sent after handshake - * is confirmed. During handshake, there is not much packets - * sent and also people seem to do lots of effort not to trigger - * persistent congestion there, then it is a lot easier to just - * not enable it during handshake. - */ - if (rtb->pktns_id == NGTCP2_PKTNS_ID_APP && loss_window > 0) { - if (loss_window >= congestion_period) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "persistent congestion loss_window=%" PRIu64 - " congestion_period=%" PRIu64, - loss_window, congestion_period); - - cc->on_persistent_congestion(cc, cstat, ts); - } - } - - break; - } - } - - ngtcp2_rtb_remove_excessive_lost_pkt(rtb, pkt_thres); - - return 0; -} - -void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { - ngtcp2_ksl_it it = ngtcp2_ksl_end(&rtb->ents); - ngtcp2_rtb_entry *ent; - int rv; - - for (; rtb->num_lost_pkts > n;) { - assert(ngtcp2_ksl_it_end(&it)); - ngtcp2_ksl_it_prev(&it); - ent = ngtcp2_ksl_it_get(&it); - - assert(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED); - - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); - - --rtb->num_lost_pkts; - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); - assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); - } -} - -void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, - ngtcp2_tstamp ts) { - ngtcp2_ksl_it it; - ngtcp2_rtb_entry *ent; - int rv; - - if (ngtcp2_ksl_len(&rtb->ents) == 0) { - return; - } - - it = ngtcp2_ksl_end(&rtb->ents); - - for (;;) { - assert(ngtcp2_ksl_it_end(&it)); - - ngtcp2_ksl_it_prev(&it); - ent = ngtcp2_ksl_it_get(&it); - - if (!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED) || - ts - ent->lost_ts < pto) { - return; - } - - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); - - --rtb->num_lost_pkts; - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); - assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); - - if (ngtcp2_ksl_len(&rtb->ents) == 0) { - return; - } - } -} - -ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb) { - ngtcp2_ksl_it it; - ngtcp2_rtb_entry *ent; - - if (ngtcp2_ksl_len(&rtb->ents) == 0) { - return UINT64_MAX; - } - - it = ngtcp2_ksl_end(&rtb->ents); - ngtcp2_ksl_it_prev(&it); - ent = ngtcp2_ksl_it_get(&it); - - if (!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED)) { - return UINT64_MAX; - } - - 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; - 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_FLAG_PROBE) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 - " is a probe packet, no retransmission is necessary", - ent->hd.pkt_num); - return 0; - } - - if (!ent->frc) { - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ - assert(!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED)); - assert(!(ent->flags & NGTCP2_RTB_FLAG_PTO_RECLAIMED)); - return 0; - } - - if (ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED) { - --rtb->num_lost_pkts; - } - - if (ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 - " was declared lost and has already been retransmitted", - ent->hd.pkt_num); - return 0; - } - - if (ent->flags & NGTCP2_RTB_FLAG_PTO_RECLAIMED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " has already been reclaimed on PTO", - ent->hd.pkt_num); - return 0; - } - - pfrc = &ent->frc; - - for (; *pfrc;) { - switch ((*pfrc)->fr.type) { - case NGTCP2_FRAME_STREAM: - frc = *pfrc; - - *pfrc = frc->next; - frc->next = NULL; - sfr = &frc->fr.stream; - - strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); - if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); - break; - } - rv = ngtcp2_strm_streamfrq_push(strm, frc); - if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - if (!ngtcp2_strm_is_tx_queued(strm)) { - strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn); - rv = ngtcp2_conn_tx_strmq_push(conn, strm); - if (rv != 0) { - return rv; - } - } - break; - case NGTCP2_FRAME_CRYPTO: - frc = *pfrc; - - *pfrc = frc->next; - frc->next = NULL; - - rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, - &frc->fr.crypto.offset, frc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); - return rv; - } - break; - default: - pfrc = &(*pfrc)->next; - } - } - - *pfrc = pktns->tx.frq; - pktns->tx.frq = ent->frc; - ent->frc = NULL; - - return 0; -} - -int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) { - ngtcp2_rtb_entry *ent; - ngtcp2_ksl_it it; - int rv; - - it = ngtcp2_ksl_begin(&rtb->ents); - - for (; !ngtcp2_ksl_it_end(&it);) { - ent = ngtcp2_ksl_it_get(&it); - - rtb_on_remove(rtb, ent, cstat); - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); - assert(0 == rv); - - rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); - ngtcp2_rtb_entry_del(ent, rtb->mem); - if (rv != 0) { - return rv; - } - } - - return 0; -} - -int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) { - return ngtcp2_ksl_len(&rtb->ents) == 0; -} - -void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num) { - rtb->cc_pkt_num = cc_pkt_num; - rtb->cc_bytes_in_flight = 0; -} - -ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, size_t num_pkts) { - ngtcp2_ksl_it it; - ngtcp2_rtb_entry *ent; - ngtcp2_ssize reclaimed; - size_t atmost = num_pkts; - - it = ngtcp2_ksl_end(&rtb->ents); - for (; !ngtcp2_ksl_it_begin(&it) && num_pkts >= 1;) { - ngtcp2_ksl_it_prev(&it); - ent = ngtcp2_ksl_it_get(&it); - - if ((ent->flags & (NGTCP2_RTB_FLAG_LOST_RETRANSMITTED | - NGTCP2_RTB_FLAG_PTO_RECLAIMED)) || - !(ent->flags & NGTCP2_RTB_FLAG_RETRANSMITTABLE)) { - continue; - } - - assert(ent->frc); - - reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); - if (reclaimed < 0) { - return reclaimed; - } - - /* Mark reclaimed even if reclaimed == 0 so that we can skip it in - the next run. */ - ent->flags |= NGTCP2_RTB_FLAG_PTO_RECLAIMED; - - assert(rtb->num_retransmittable); - --rtb->num_retransmittable; - - if (reclaimed) { - --num_pkts; - } - } - - return (ngtcp2_ssize)(atmost - num_pkts); -} diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/lib/ngtcp2_rtb.h deleted file mode 100644 index 0d0b738f396..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_rtb.h +++ /dev/null @@ -1,408 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_RTB_H -#define NGTCP2_RTB_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_pkt.h" -#include "ngtcp2_ksl.h" -#include "ngtcp2_pq.h" - -struct ngtcp2_conn; -typedef struct ngtcp2_conn ngtcp2_conn; - -typedef struct ngtcp2_pktns ngtcp2_pktns; - -struct ngtcp2_frame_chain; -typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; - -struct ngtcp2_log; -typedef struct ngtcp2_log ngtcp2_log; - -struct ngtcp2_qlog; -typedef struct ngtcp2_qlog ngtcp2_qlog; - -struct ngtcp2_strm; -typedef struct ngtcp2_strm ngtcp2_strm; - -struct ngtcp2_rst; -typedef struct ngtcp2_rst ngtcp2_rst; - -typedef enum ngtcp2_frame_chain_binder_flag { - NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE = 0x00, - /* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information - which a frame carries has been acknowledged. */ - NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK = 0x01, -} ngtcp2_frame_chain_binder_flag; - -/* - * ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to - * share the acknowledgement state. In general, all - * ngtcp2_frame_chains bound to the same binder must have the same - * information. - */ -typedef struct ngtcp2_frame_chain_binder { - size_t refcount; - uint32_t flags; -} ngtcp2_frame_chain_binder; - -int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder, - const ngtcp2_mem *mem); - -/* - * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using - * new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL - * a->binder. |b| must not have non-NULL b->binder. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, - const ngtcp2_mem *mem); - -/* - * ngtcp2_frame_chain chains frames in a single packet. - */ -struct ngtcp2_frame_chain { - ngtcp2_frame_chain *next; - ngtcp2_frame_chain_binder *binder; - ngtcp2_frame fr; -}; - -/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that - a ngtcp2_stream can include. */ -#define NGTCP2_MAX_STREAM_DATACNT 256 - -/* NGTCP2_MAX_CRYPTO_DATACNT is the maximum number of ngtcp2_vec that - a ngtcp2_crypto can include. */ -#define NGTCP2_MAX_CRYPTO_DATACNT 8 - -/* - * ngtcp2_frame_chain_new allocates ngtcp2_frame_chain object and - * assigns its pointer to |*pfrc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem); - -/* - * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new, - * but it allocates extra memory |extralen| in order to extend - * ngtcp2_frame. - */ -int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, - const ngtcp2_mem *mem); - -/* - * ngtcp2_frame_chain_stream_datacnt_new works like - * ngtcp2_frame_chain_new, but it allocates enough data to store - * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. - */ -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); - -/* - * ngtcp2_frame_chain_crypto_datacnt_new works like - * ngtcp2_frame_chain_new, but it allocates enough data to store - * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. - */ -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); - -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem); - -/* - * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the - * memory pointed by |frc|. - */ -void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem); - -/* - * ngtcp2_frame_chain_init initializes |frc|. - */ -void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc); - -/* - * ngtcp2_frame_chain_list_del deletes |frc|, and all objects - * connected by next field. - */ -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem); - -typedef enum { - NGTCP2_RTB_FLAG_NONE = 0x00, - /* NGTCP2_RTB_FLAG_PROBE indicates that the entry includes a probe - packet. */ - NGTCP2_RTB_FLAG_PROBE = 0x01, - /* NGTCP2_RTB_FLAG_RETRANSMITTABLE indicates that the entry includes - a frame which must be retransmitted until it is acknowledged. In - most cases, this flag is used along with - NGTCP2_RTB_FLAG_ACK_ELICITING. We have these 2 flags because - NGTCP2_RTB_FLAG_RETRANSMITTABLE triggers PTO, but just - NGTCP2_RTB_FLAG_ACK_ELICITING does not. */ - NGTCP2_RTB_FLAG_RETRANSMITTABLE = 0x02, - /* NGTCP2_RTB_FLAG_ACK_ELICITING indicates that the entry elicits - acknowledgement. */ - NGTCP2_RTB_FLAG_ACK_ELICITING = 0x04, - /* NGTCP2_RTB_FLAG_PTO_RECLAIMED indicates that the packet has been - reclaimed on PTO. It is not marked lost yet and still consumes - congestion window. */ - NGTCP2_RTB_FLAG_PTO_RECLAIMED = 0x08, - /* NGTCP2_RTB_FLAG_LOST_RETRANSMITTED indicates that the entry has - been marked lost and scheduled to retransmit. */ - NGTCP2_RTB_FLAG_LOST_RETRANSMITTED = 0x10, -} ngtcp2_rtb_flag; - -struct ngtcp2_rtb_entry; -typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; - -/* - * ngtcp2_rtb_entry is an object stored in ngtcp2_rtb. It corresponds - * to the one packet which is waiting for its ACK. - */ -struct ngtcp2_rtb_entry { - ngtcp2_rtb_entry *next; - - struct { - int64_t pkt_num; - uint8_t type; - uint8_t flags; - } hd; - ngtcp2_frame_chain *frc; - /* ts is the time point when a packet included in this entry is sent - to a peer. */ - ngtcp2_tstamp ts; - /* lost_ts is the time when this entry is marked lost. */ - ngtcp2_tstamp lost_ts; - /* pktlen is the length of QUIC packet */ - size_t pktlen; - struct { - uint64_t delivered; - ngtcp2_tstamp delivered_ts; - ngtcp2_tstamp first_sent_ts; - int is_app_limited; - } rst; - /* flags is bitwise-OR of zero or more of ngtcp2_rtb_flag. */ - uint8_t flags; -}; - -/* - * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns - * its pointer to |*pent|. On success, |*pent| takes ownership of - * |frc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem); - -/* - * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory - * pointed by |ent|. - */ -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem); - -/* - * ngtcp2_rtb tracks sent packets, and its ACK timeout for - * retransmission. - */ -typedef struct { - /* ents includes ngtcp2_rtb_entry sorted by decreasing order of - packet number. */ - ngtcp2_ksl ents; - /* crypto is CRYPTO stream. */ - ngtcp2_strm *crypto; - ngtcp2_rst *rst; - ngtcp2_cc *cc; - ngtcp2_log *log; - ngtcp2_qlog *qlog; - const ngtcp2_mem *mem; - /* largest_acked_tx_pkt_num is the largest packet number - acknowledged by the peer. */ - int64_t largest_acked_tx_pkt_num; - /* num_ack_eliciting is the number of ACK eliciting entries. */ - size_t num_ack_eliciting; - /* num_retransmittable is the number of packets which contain frames - that must be retransmitted on loss. */ - size_t num_retransmittable; - /* probe_pkt_left is the number of probe packet to send */ - size_t probe_pkt_left; - /* pktns_id is the identifier of packet number space. */ - ngtcp2_pktns_id pktns_id; - /* cc_pkt_num is the smallest packet number that is contributed to - ngtcp2_conn_stat.bytes_in_flight. */ - int64_t cc_pkt_num; - /* cc_bytes_in_flight is the number of in-flight bytes that is - contributed to ngtcp2_conn_stat.bytes_in_flight. It only - includes the bytes after congestion state is reset. */ - uint64_t cc_bytes_in_flight; - /* persistent_congestion_start_ts is the time when persistent - congestion evaluation is started. It happens roughly after - handshake is confirmed. */ - ngtcp2_tstamp persistent_congestion_start_ts; - /* num_lost_pkts is the number entries in ents which has - NGTCP2_RTB_FLAG_LOST_RETRANSMITTED flag set. */ - size_t num_lost_pkts; -} ngtcp2_rtb; - -/* - * ngtcp2_rtb_init initializes |rtb|. - */ -void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, - ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, - ngtcp2_log *log, ngtcp2_qlog *qlog, const ngtcp2_mem *mem); - -/* - * ngtcp2_rtb_free deallocates resources allocated for |rtb|. - */ -void ngtcp2_rtb_free(ngtcp2_rtb *rtb); - -/* - * ngtcp2_rtb_add adds |ent| to |rtb|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat); - -/* - * ngtcp2_rtb_head returns the iterator which points to the entry - * which has the largest packet number. If there is no entry, - * returned value satisfies ngtcp2_ksl_it_end(&it) != 0. - */ -ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb); - -/* - * ngtcp2_rtb_recv_ack removes acked ngtcp2_rtb_entry from |rtb|. - * |pkt_num| is a packet number which includes |fr|. |pkt_ts| is the - * timestamp when packet is received. |ts| should be the current - * time. Usually they are the same, but for buffered packets, - * |pkt_ts| would be earlier than |ts|. - * - * This function returns the number of newly acknowledged packets if - * it succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed - * NGTCP2_ERR_NOMEM - * Out of memory - */ -ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, - ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, - ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts); - -/* - * ngtcp2_rtb_detect_lost_pkt detects lost packets and prepends the - * frames contained them to |*pfrc|. Even when this function fails, - * some frames might be prepended to |*pfrc| and the caller should - * handle them. |pto| is PTO. - */ -int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, - ngtcp2_duration pto, ngtcp2_tstamp ts); - -/* - * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. - */ -void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, - ngtcp2_tstamp ts); - -/* - * ngtcp2_rtb_lost_pkt_ts returns the earliest time when the still - * retained packet was lost. It returns UINT64_MAX if no such packet - * exists. - */ -ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(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. - */ -int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); - -/* - * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry. - */ -int ngtcp2_rtb_empty(ngtcp2_rtb *rtb); - -/* - * ngtcp2_rtb_reset_cc_state resets congestion state in |rtb|. - * |cc_pkt_num| is the next outbound packet number which is sent under - * new congestion state. - */ -void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num); - -/* - * ngtcp2_rtb_remove_expired_lost_pkt ensures that the number of lost - * packets at most |n|. - */ -void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n); - -/* - * ngtcp2_rtb_reclaim_on_pto reclaims up to |num_pkts| packets which - * are in-flight and not marked lost to send them in PTO probe. The - * reclaimed frames are chained to |*pfrc|. - * - * This function returns the number of packets reclaimed if it - * succeeds, or one of the following negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, size_t num_pkts); - -#endif /* NGTCP2_RTB_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/lib/ngtcp2_str.c deleted file mode 100644 index b7502e3bd0a..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_str.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_str.h" - -#include <string.h> - -void *ngtcp2_cpymem(void *dest, const void *src, size_t n) { - memcpy(dest, src, n); - return (uint8_t *)dest + n; -} - -uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n) { - memset(dest, b, n); - return dest + n; -} - -#define LOWER_XDIGITS "0123456789abcdef" - -uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len) { - size_t i; - uint8_t *p = dest; - - for (i = 0; i < len; ++i) { - *p++ = (uint8_t)LOWER_XDIGITS[data[i] >> 4]; - *p++ = (uint8_t)LOWER_XDIGITS[data[i] & 0xf]; - } - - *p = '\0'; - - return dest; -} - -char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, - size_t len) { - size_t i; - char *p = dest; - uint8_t c; - - for (i = 0; i < len; ++i) { - c = data[i]; - if (0x20 <= c && c <= 0x7e) { - *p++ = (char)c; - } else { - *p++ = '.'; - } - } - - *p = '\0'; - - return dest; -} - -int ngtcp2_verify_stateless_reset_token(const uint8_t *want, - const uint8_t *got) { - return !ngtcp2_check_invalid_stateless_reset_token(got) && - ngtcp2_cmemeq(want, got, NGTCP2_STATELESS_RESET_TOKENLEN) - ? 0 - : NGTCP2_ERR_INVALID_ARGUMENT; -} - -int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token) { - static uint8_t invalid_token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0}; - - return 0 == memcmp(invalid_token, token, NGTCP2_STATELESS_RESET_TOKENLEN); -} - -int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) { - size_t i; - int rv = 0; - - for (i = 0; i < n; ++i) { - rv |= a[i] ^ b[i]; - } - - return rv == 0; -} diff --git a/deps/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/lib/ngtcp2_str.h deleted file mode 100644 index 104b1f1a038..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_str.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_STR_H -#define NGTCP2_STR_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -void *ngtcp2_cpymem(void *dest, const void *src, size_t n); - -/* - * ngtcp2_setmem writes a string of length |n| consisting only |b| to - * the buffer pointed by |dest|. It returns dest + n; - */ -uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n); -/* - * ngtcp2_encode_hex encodes |data| of length |len| in hex string. It - * writes additional NULL bytes at the end of the buffer. The buffer - * pointed by |dest| must have at least |len| * 2 + 1 bytes space. - * This function returns |dest|. - */ -uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len); - -/* - * ngtcp2_encode_printable_ascii encodes |data| of length |len| in - * |dest| in the following manner: printable ascii characters are - * copied as is. The other characters are converted to ".". It - * writes additional NULL bytes at the end of the buffer. |dest| must - * have at least |len| + 1 bytes. This function returns |dest|. - */ -char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, - size_t len); - -/* - * ngtcp2_verify_stateless_reset_token verifies stateless reset token - * |want| and |got|. This function returns 0 if |want| equals |got| - * and |got| is not all zero, or one of the following negative error - * codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Token does not match; or token is all zero. - */ -int ngtcp2_verify_stateless_reset_token(const uint8_t *want, - const uint8_t *got); - -/* - * ngtcp2_check_invalid_stateless_reset_token returns nonzero if - * |token| is invalid stateless reset token. Currently, token which - * consists of all zeros is considered invalid. - */ -int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token); - -/* - * ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers - * pointed by |a| and |b| are equal. The comparison is done in a - * constant time manner. - */ -int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n); - -#endif /* NGTCP2_STR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/lib/ngtcp2_strm.c deleted file mode 100644 index 6fb73dc0727..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_strm.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_strm.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_rtb.h" -#include "ngtcp2_pkt.h" -#include "ngtcp2_vec.h" - -static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { - return *(int64_t *)lhs < *(int64_t *)rhs; -} - -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem) { - strm->cycle = 0; - strm->tx.acked_offset = NULL; - strm->tx.cont_acked_offset = 0; - strm->tx.streamfrq = NULL; - strm->tx.offset = 0; - strm->tx.max_offset = max_tx_offset; - strm->rx.rob = NULL; - strm->rx.cont_offset = 0; - strm->rx.last_offset = 0; - strm->stream_id = stream_id; - strm->flags = flags; - strm->stream_user_data = stream_user_data; - strm->rx.max_offset = strm->rx.unsent_max_offset = max_rx_offset; - strm->me.key = (uint64_t)stream_id; - strm->me.next = NULL; - strm->pe.index = NGTCP2_PQ_BAD_INDEX; - strm->mem = mem; - strm->app_error_code = 0; - - return 0; -} - -void ngtcp2_strm_free(ngtcp2_strm *strm) { - ngtcp2_ksl_it it; - - if (strm == NULL) { - return; - } - - if (strm->tx.streamfrq) { - for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem); - } - - ngtcp2_ksl_free(strm->tx.streamfrq); - ngtcp2_mem_free(strm->mem, strm->tx.streamfrq); - } - - ngtcp2_rob_free(strm->rx.rob); - ngtcp2_mem_free(strm->mem, strm->rx.rob); - ngtcp2_gaptr_free(strm->tx.acked_offset); - ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); -} - -static int strm_rob_init(ngtcp2_strm *strm) { - int rv; - ngtcp2_rob *rob = ngtcp2_mem_malloc(strm->mem, sizeof(*rob)); - - if (rob == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rv = ngtcp2_rob_init(rob, 8 * 1024, strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, rob); - return rv; - } - - strm->rx.rob = rob; - - return 0; -} - -uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm) { - if (strm->rx.rob == NULL) { - return strm->rx.cont_offset; - } - return ngtcp2_rob_first_gap_offset(strm->rx.rob); -} - -int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, - size_t datalen, uint64_t offset) { - int rv; - - if (strm->rx.rob == NULL) { - rv = strm_rob_init(strm); - if (rv != 0) { - return rv; - } - - if (strm->rx.cont_offset) { - rv = ngtcp2_rob_remove_prefix(strm->rx.rob, strm->rx.cont_offset); - if (rv != 0) { - return rv; - } - } - } - - return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); -} - -int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) { - if (strm->rx.rob == NULL) { - strm->rx.cont_offset = offset; - return 0; - } - - return ngtcp2_rob_remove_prefix(strm->rx.rob, offset); -} - -void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { - strm->flags |= flags & NGTCP2_STRM_FLAG_SHUT_RDWR; -} - -static int strm_streamfrq_init(ngtcp2_strm *strm) { - int rv; - ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq)); - if (streamfrq == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rv = ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, streamfrq); - return rv; - } - - strm->tx.streamfrq = streamfrq; - - return 0; -} - -int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) { - int rv; - - assert(frc->fr.type == NGTCP2_FRAME_STREAM); - assert(frc->next == NULL); - - if (strm->tx.streamfrq == NULL) { - rv = strm_streamfrq_init(strm); - if (rv != 0) { - return rv; - } - } - - return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset, - frc); -} - -static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, - ngtcp2_frame_chain **pfrc) { - ngtcp2_frame_chain *frc, *nfrc; - ngtcp2_stream *fr, *nfr; - uint64_t offset, end_offset; - size_t idx, end_idx; - uint64_t base_offset, end_base_offset; - ngtcp2_range gap; - ngtcp2_vec *v; - int rv; - ngtcp2_ksl_it it; - - *pfrc = NULL; - - assert(strm->tx.streamfrq); - assert(ngtcp2_ksl_len(strm->tx.streamfrq)); - - for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);) { - frc = ngtcp2_ksl_it_get(&it); - fr = &frc->fr.stream; - - ngtcp2_ksl_remove(strm->tx.streamfrq, &it, &fr->offset); - - idx = 0; - offset = fr->offset; - base_offset = 0; - - gap = ngtcp2_strm_get_unacked_range_after(strm, offset); - if (gap.begin < offset) { - gap.begin = offset; - } - - for (; idx < fr->datacnt && offset < gap.begin; ++idx) { - v = &fr->data[idx]; - if (offset + v->len > gap.begin) { - base_offset = gap.begin - offset; - break; - } - - offset += v->len; - } - - if (idx == fr->datacnt) { - if (fr->fin) { - if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { - ngtcp2_frame_chain_del(frc, strm->mem); - assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0); - return 0; - } - - fr->offset = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt); - fr->datacnt = 0; - - *pfrc = frc; - - return 0; - } - ngtcp2_frame_chain_del(frc, strm->mem); - continue; - } - - assert(gap.begin == offset + base_offset); - - end_idx = idx; - end_offset = offset; - end_base_offset = 0; - - for (; end_idx < fr->datacnt; ++end_idx) { - v = &fr->data[end_idx]; - if (end_offset + v->len > gap.end) { - end_base_offset = gap.end - end_offset; - break; - } - - end_offset += v->len; - } - - if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) { - *pfrc = frc; - return 0; - } - - if (fr->datacnt == end_idx) { - memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); - - assert(fr->data[0].len > base_offset); - - fr->offset = offset + base_offset; - fr->datacnt = end_idx - idx; - fr->data[0].base += base_offset; - fr->data[0].len -= (size_t)base_offset; - - *pfrc = frc; - return 0; - } - - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx, - strm->mem); - if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - - nfr = &nfrc->fr.stream; - memcpy(nfr->data, fr->data + end_idx, - sizeof(nfr->data[0]) * (fr->datacnt - end_idx)); - - assert(nfr->data[0].len > end_base_offset); - - nfr->type = NGTCP2_FRAME_STREAM; - nfr->flags = 0; - nfr->fin = fr->fin; - nfr->stream_id = fr->stream_id; - nfr->offset = end_offset + end_base_offset; - nfr->datacnt = fr->datacnt - end_idx; - nfr->data[0].base += end_base_offset; - nfr->data[0].len -= (size_t)end_base_offset; - - rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - - if (end_base_offset) { - ++end_idx; - } - - memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); - - assert(fr->data[0].len > base_offset); - - fr->fin = 0; - fr->offset = offset + base_offset; - fr->datacnt = end_idx - idx; - if (end_base_offset) { - assert(fr->data[fr->datacnt - 1].len > end_base_offset); - fr->data[fr->datacnt - 1].len = (size_t)end_base_offset; - } - fr->data[0].base += base_offset; - fr->data[0].len -= (size_t)base_offset; - - *pfrc = frc; - return 0; - } - - return 0; -} - -int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, - size_t left) { - ngtcp2_stream *fr, *nfr; - ngtcp2_frame_chain *frc, *nfrc; - int rv; - size_t nmerged; - size_t datalen; - ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT]; - ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT]; - size_t acnt, bcnt; - uint64_t unacked_offset; - - if (strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0) { - *pfrc = NULL; - return 0; - } - - rv = strm_streamfrq_unacked_pop(strm, &frc); - if (rv != 0) { - return rv; - } - if (frc == NULL) { - *pfrc = NULL; - return 0; - } - - fr = &frc->fr.stream; - datalen = ngtcp2_vec_len(fr->data, fr->datacnt); - - if (left == 0) { - /* datalen could be zero if 0 length STREAM has been sent */ - if (datalen || ngtcp2_ksl_len(strm->tx.streamfrq) > 1) { - rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - *pfrc = NULL; - return 0; - } - } - - if (datalen > left) { - ngtcp2_vec_copy(a, fr->data, fr->datacnt); - acnt = fr->datacnt; - - bcnt = 0; - ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_STREAM_DATACNT); - - assert(acnt > 0); - assert(bcnt > 0); - - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - - nfr = &nfrc->fr.stream; - nfr->type = NGTCP2_FRAME_STREAM; - nfr->flags = 0; - nfr->fin = fr->fin; - nfr->stream_id = fr->stream_id; - nfr->offset = fr->offset + left; - nfr->datacnt = bcnt; - ngtcp2_vec_copy(nfr->data, b, bcnt); - - rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - - nfr = &nfrc->fr.stream; - *nfr = *fr; - nfr->fin = 0; - nfr->datacnt = acnt; - ngtcp2_vec_copy(nfr->data, a, acnt); - - ngtcp2_frame_chain_del(frc, strm->mem); - - *pfrc = nfrc; - - return 0; - } - - left -= datalen; - - ngtcp2_vec_copy(a, fr->data, fr->datacnt); - acnt = fr->datacnt; - - for (; left && ngtcp2_ksl_len(strm->tx.streamfrq);) { - unacked_offset = ngtcp2_strm_streamfrq_unacked_offset(strm); - if (unacked_offset != fr->offset + datalen) { - assert(fr->offset + datalen < unacked_offset); - break; - } - - rv = strm_streamfrq_unacked_pop(strm, &nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - if (nfrc == NULL) { - break; - } - - nfr = &nfrc->fr.stream; - - if (nfr->fin && nfr->datacnt == 0) { - fr->fin = 1; - ngtcp2_frame_chain_del(nfrc, strm->mem); - break; - } - - nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left, - NGTCP2_MAX_STREAM_DATACNT); - if (nmerged == 0) { - rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - break; - } - - datalen += nmerged; - left -= nmerged; - - if (nfr->datacnt == 0) { - fr->fin = nfr->fin; - ngtcp2_frame_chain_del(nfrc, strm->mem); - continue; - } - - nfr->offset += nmerged; - - rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); - if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - - break; - } - - if (acnt == fr->datacnt) { - if (acnt > 0) { - fr->data[acnt - 1] = a[acnt - 1]; - } - - *pfrc = frc; - return 0; - } - - assert(acnt > fr->datacnt); - - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); - if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); - return rv; - } - - nfr = &nfrc->fr.stream; - *nfr = *fr; - nfr->datacnt = acnt; - ngtcp2_vec_copy(nfr->data, a, acnt); - - ngtcp2_frame_chain_del(frc, strm->mem); - - *pfrc = nfrc; - - return 0; -} - -uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm) { - ngtcp2_frame_chain *frc; - ngtcp2_stream *fr; - ngtcp2_range gap; - ngtcp2_ksl_it it; - size_t datalen; - - assert(strm->tx.streamfrq); - assert(ngtcp2_ksl_len(strm->tx.streamfrq)); - - for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - frc = ngtcp2_ksl_it_get(&it); - fr = &frc->fr.stream; - - gap = ngtcp2_strm_get_unacked_range_after(strm, fr->offset); - - datalen = ngtcp2_vec_len(fr->data, fr->datacnt); - - if (gap.begin <= fr->offset) { - return fr->offset; - } - if (gap.begin < fr->offset + datalen) { - return gap.begin; - } - if (fr->offset + datalen == gap.begin && fr->fin && - !(strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED)) { - return fr->offset + datalen; - } - } - - return (uint64_t)-1; -} - -ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm) { - ngtcp2_ksl_it it; - - assert(strm->tx.streamfrq); - assert(ngtcp2_ksl_len(strm->tx.streamfrq)); - - it = ngtcp2_ksl_begin(strm->tx.streamfrq); - return ngtcp2_ksl_it_get(&it); -} - -int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm) { - return strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0; -} - -void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { - ngtcp2_frame_chain *frc; - ngtcp2_ksl_it it; - - if (strm->tx.streamfrq == NULL) { - return; - } - - for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, strm->mem); - } - ngtcp2_ksl_clear(strm->tx.streamfrq); -} - -int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm) { - return strm->pe.index != NGTCP2_PQ_BAD_INDEX; -} - -int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) { - if (strm->tx.acked_offset == NULL) { - return strm->tx.cont_acked_offset == strm->tx.offset; - } - - return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset) == - strm->tx.offset; -} - -ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, - uint64_t offset) { - ngtcp2_ksl_it gapit; - ngtcp2_range gap; - - if (strm->tx.acked_offset == NULL) { - gap.begin = strm->tx.cont_acked_offset; - gap.end = UINT64_MAX; - return gap; - } - - gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); - return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit); -} - -uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { - if (strm->tx.acked_offset == NULL) { - return strm->tx.cont_acked_offset; - } - - return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset); -} - -static int strm_acked_offset_init(ngtcp2_strm *strm) { - int rv; - ngtcp2_gaptr *acked_offset = - ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset)); - - if (acked_offset == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rv = ngtcp2_gaptr_init(acked_offset, strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, acked_offset); - return rv; - } - - strm->tx.acked_offset = acked_offset; - - return 0; -} - -int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) { - int rv; - - if (strm->tx.acked_offset == NULL) { - if (strm->tx.cont_acked_offset == offset) { - strm->tx.cont_acked_offset += len; - return 0; - } - - rv = strm_acked_offset_init(strm); - if (rv != 0) { - return rv; - } - - rv = - ngtcp2_gaptr_push(strm->tx.acked_offset, 0, strm->tx.cont_acked_offset); - if (rv != 0) { - return rv; - } - } - - return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); -} diff --git a/deps/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/lib/ngtcp2_strm.h deleted file mode 100644 index 7da8437e668..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_strm.h +++ /dev/null @@ -1,266 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2017 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_STRM_H -#define NGTCP2_STRM_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#include "ngtcp2_rob.h" -#include "ngtcp2_map.h" -#include "ngtcp2_gaptr.h" -#include "ngtcp2_ksl.h" -#include "ngtcp2_pq.h" - -struct ngtcp2_frame_chain; -typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; - -typedef enum { - NGTCP2_STRM_FLAG_NONE = 0, - /* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of - stream data is not allowed. */ - NGTCP2_STRM_FLAG_SHUT_RD = 0x01, - /* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of - stream data is not allowed. */ - NGTCP2_STRM_FLAG_SHUT_WR = 0x02, - NGTCP2_STRM_FLAG_SHUT_RDWR = - NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR, - /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is sent from - the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is - also set. */ - NGTCP2_STRM_FLAG_SENT_RST = 0x04, - /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is received - from the remote endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_RD - is also set. */ - NGTCP2_STRM_FLAG_RECV_RST = 0x08, - /* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent - from the local endpoint. */ - NGTCP2_STRM_FLAG_STOP_SENDING = 0x10, - /* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM - is acknowledged by peer. */ - NGTCP2_STRM_FLAG_RST_ACKED = 0x20, - /* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit - set is acknowledged by a remote endpoint. */ - NGTCP2_STRM_FLAG_FIN_ACKED = 0x40, -} ngtcp2_strm_flags; - -struct ngtcp2_strm; -typedef struct ngtcp2_strm ngtcp2_strm; - -struct ngtcp2_strm { - ngtcp2_map_entry me; - ngtcp2_pq_entry pe; - uint64_t cycle; - - struct { - /* acked_offset tracks acknowledged outgoing data. */ - ngtcp2_gaptr *acked_offset; - /* cont_acked_offset is the offset that all data up to this offset - is acknowledged by a remote endpoint. It is used until the - remote endpoint acknowledges data in out-of-order. After that, - acked_offset is used instead. */ - uint64_t cont_acked_offset; - /* streamfrq contains STREAM frame for retransmission. The flow - control credits have been paid when they are transmitted first - time. There are no restriction regarding flow control for - retransmission. */ - ngtcp2_ksl *streamfrq; - /* offset is the next offset of outgoing data. In other words, it - is the number of bytes sent in this stream without - duplication. */ - uint64_t offset; - /* max_tx_offset is the maximum offset that local endpoint can - send for this stream. */ - uint64_t max_offset; - } tx; - - struct { - /* rob is the reorder buffer for incoming stream data. The data - received in out of order is buffered and sorted by its offset - in this object. */ - ngtcp2_rob *rob; - /* cont_offset is the largest offset of consecutive data. It is - used until the endpoint receives out-of-order data. After - that, rob is used to track the offset and data. */ - uint64_t cont_offset; - /* last_offset is the largest offset of stream data received for - this stream. */ - uint64_t last_offset; - /* max_offset is the maximum offset that remote endpoint can send - to this stream. */ - uint64_t max_offset; - /* unsent_max_offset is the maximum offset that remote endpoint - can send to this stream, and it is not notified to the remote - endpoint. unsent_max_offset >= max_offset must be hold. */ - uint64_t unsent_max_offset; - } rx; - - const ngtcp2_mem *mem; - int64_t stream_id; - void *stream_user_data; - /* flags is bit-wise OR of zero or more of ngtcp2_strm_flags. */ - uint32_t flags; - /* app_error_code is an error code the local endpoint sent in - RST_STREAM or STOP_SENDING. */ - uint64_t app_error_code; -}; - -/* - * ngtcp2_strm_init initializes |strm|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem); - -/* - * ngtcp2_strm_free deallocates memory allocated for |strm|. This - * function does not free the memory pointed by |strm| itself. - */ -void ngtcp2_strm_free(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_rx_offset returns the minimum offset of stream data - * which is not received yet. - */ -uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_recv_reordering handles reordered data. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, - size_t datalen, uint64_t offset); - -/* - * ngtcp2_strm_update_rx_offset tells that data up to offset bytes are - * received in order. - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset); - -/* - * ngtcp2_strm_shutdown shutdowns |strm|. |flags| should be - * NGTCP2_STRM_FLAG_SHUT_RD, and/or NGTCP2_STRM_FLAG_SHUT_WR. - */ -void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags); - -/* - * ngtcp2_strm_streamfrq_push pushes |frc| to streamfrq for - * retransmission. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc); - -/* - * ngtcp2_strm_streamfrq_pop pops the first ngtcp2_frame_chain and - * assigns it to |*pfrc|. This function splits into or merges several - * ngtcp2_frame_chain objects so that the returned ngtcp2_frame_chain - * has at most |left| data length. If there is no frames to send, - * this function returns 0 and |*pfrc| is NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - */ -int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, - size_t left); - -/* - * ngtcp2_strm_streamfrq_unacked_offset returns the smallest offset of - * unacknowledged stream data held in strm->tx.streamfrq. - */ -uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_streamfrq_top returns the first ngtcp2_frame_chain. - * The queue must not be empty. - */ -ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_streamfrq_empty returns nonzero if streamfrq is empty. - */ -int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_streamfrq_clear removes all frames from streamfrq. - */ -void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_is_tx_queued returns nonzero if |strm| is queued. - */ -int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_is_all_tx_data_acked returns nonzero if all outgoing - * data for |strm| which have sent so far have been acknowledged. - */ -int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_get_unacked_range_after returns the range that is not - * acknowledged yet and intersects or comes after |offset|. - */ -ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, - uint64_t offset); - -/* - * ngtcp2_strm_get_acked_offset returns offset, that is the data up to - * this offset have been acknowledged by a remote endpoint. It - * returns 0 if no data is acknowledged. - */ -uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm); - -/* - * ngtcp2_strm_ack_data tells |strm| that the data [offset, - * offset+len) is acknowledged by a remote endpoint. - */ -int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len); - -#endif /* NGTCP2_STRM_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/lib/ngtcp2_vec.c deleted file mode 100644 index 7a6f8afa051..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_vec.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_vec.h" - -#include <string.h> -#include <assert.h> - -#include "ngtcp2_str.h" - -ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) { - vec->base = (uint8_t *)base; - vec->len = len; - return vec; -} - -int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, - const ngtcp2_mem *mem) { - size_t len; - uint8_t *p; - - len = sizeof(ngtcp2_vec) + datalen; - - *pvec = ngtcp2_mem_malloc(mem, len); - if (*pvec == NULL) { - return NGTCP2_ERR_NOMEM; - } - - p = (uint8_t *)(*pvec) + sizeof(ngtcp2_vec); - (*pvec)->base = p; - (*pvec)->len = datalen; - if (datalen) { - /* p = */ ngtcp2_cpymem(p, data, datalen); - } - - return 0; -} - -void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, vec); -} - -size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { - size_t i; - size_t res = 0; - - for (i = 0; i < n; ++i) { - res += vec[i].len; - } - - return res; -} - -ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, - size_t *pdstcnt, size_t left, size_t maxcnt) { - size_t i; - size_t srccnt = *psrccnt; - size_t nmove; - size_t extra = 0; - - for (i = 0; i < srccnt; ++i) { - if (left >= src[i].len) { - left -= src[i].len; - continue; - } - - if (*pdstcnt && src[srccnt - 1].base + src[srccnt - 1].len == dst[0].base) { - if (*pdstcnt + srccnt - i - 1 > maxcnt) { - return -1; - } - - dst[0].len += src[srccnt - 1].len; - dst[0].base = src[srccnt - 1].base; - extra = src[srccnt - 1].len; - --srccnt; - } else if (*pdstcnt + srccnt - i > maxcnt) { - return -1; - } - - if (left == 0) { - *psrccnt = i; - } else { - *psrccnt = i + 1; - } - - nmove = srccnt - i; - if (nmove) { - memmove(dst + nmove, dst, sizeof(ngtcp2_vec) * (*pdstcnt)); - *pdstcnt += nmove; - memcpy(dst, src + i, sizeof(ngtcp2_vec) * nmove); - } - - dst[0].len -= left; - dst[0].base += left; - src[i].len = left; - - if (nmove == 0) { - extra -= left; - } - - return (ngtcp2_ssize)(ngtcp2_vec_len(dst, nmove) + extra); - } - - return 0; -} - -size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, - size_t *psrccnt, size_t left, size_t maxcnt) { - size_t orig_left = left; - size_t i; - ngtcp2_vec *a, *b; - - assert(maxcnt); - - if (*pdstcnt == 0) { - if (*psrccnt == 0) { - return 0; - } - - a = &dst[0]; - b = &src[0]; - - if (left >= b->len) { - *a = *b; - ++*pdstcnt; - left -= b->len; - i = 1; - } else { - a->len = left; - a->base = b->base; - - b->len -= left; - b->base += left; - - return left; - } - } else { - i = 0; - } - - for (; left && i < *psrccnt; ++i) { - a = &dst[*pdstcnt - 1]; - b = &src[i]; - - if (left >= b->len) { - if (a->base + a->len == b->base) { - a->len += b->len; - } else if (*pdstcnt == maxcnt) { - break; - } else { - dst[(*pdstcnt)++] = *b; - } - left -= b->len; - continue; - } - - if (a->base + a->len == b->base) { - a->len += left; - } else if (*pdstcnt == maxcnt) { - break; - } else { - dst[*pdstcnt].len = left; - dst[*pdstcnt].base = b->base; - ++*pdstcnt; - } - - b->len -= left; - b->base += left; - left = 0; - - break; - } - - memmove(src, src + i, sizeof(ngtcp2_vec) * (*psrccnt - i)); - *psrccnt -= i; - - return orig_left - left; -} - -size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, - size_t dstcnt, const ngtcp2_vec *src, - size_t srccnt, size_t left) { - size_t i, j; - size_t len = left; - - *pnwritten = 0; - - for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) { - if (src[i].len == 0) { - ++i; - continue; - } - dst[j] = src[i]; - if (dst[j].len > left) { - dst[j].len = left; - *pnwritten = len; - return j + 1; - } - left -= dst[j].len; - ++i; - ++j; - } - - *pnwritten = len - left; - - return j; -} - -void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt) { - memcpy(dst, src, sizeof(ngtcp2_vec) * cnt); -} diff --git a/deps/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/lib/ngtcp2_vec.h deleted file mode 100644 index 077820a9efe..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_vec.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2018 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_VEC_H -#define NGTCP2_VEC_H - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -#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|. - */ -ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len); - -/* - * ngtcp2_vec_new allocates and initializes |*pvec| with given |data| - * of length |datalen|. This function allocates memory for |*pvec| - * and the given data with a single allocation, and the contents - * pointed by |data| is copied into the allocated memory space. To - * free the allocated memory, call ngtcp2_vec_del. - */ -int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, - const ngtcp2_mem *mem); - -/* - * ngtcp2_vec_del frees the memory allocated by |vec| which is - * allocated and initialized by ngtcp2_vec_new. - */ -void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem); - -/* - * ngtcp2_vec_len returns the sum of length in |vec| of |n| elements. - */ -size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); - -/* - * ngtcp2_vec_split splits |src| to |dst| so that the sum of the - * length in |src| does not exceed |left| bytes. The |maxcnt| is the - * maximum number of elements which |dst| array can contain. The - * caller must set |*psrccnt| to the number of elements of |src|. - * Similarly, the caller must set |*pdstcnt| to the number of elements - * of |dst|. The split does not necessarily occur at the boundary of - * ngtcp2_vec object. After split has done, this function updates - * |*psrccnt| and |*pdstcnt|. This function returns the number of - * bytes moved from |src| to |dst|. If split cannot be made because - * doing so exceeds |maxcnt|, this function returns -1. - */ -ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, - size_t *pdstcnt, size_t left, size_t maxcnt); - -/* - * ngtcp2_vec_merge merges |src| into |dst| by moving at most |left| - * bytes from |src|. The |maxcnt| is the maximum number of elements - * which |dst| array can contain. The caller must set |*pdstcnt| to - * the number of elements of |dst|. Similarly, the caller must set - * |*psrccnt| to the number of elements of |src|. After merge has - * done, this function updates |*psrccnt| and |*pdstcnt|. This - * function returns the number of bytes moved from |src| to |dst|. - */ -size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, - size_t *psrccnt, size_t left, size_t maxcnt); - -/* - * ngtcp2_vec_copy_at_most copies |src| of length |srccnt| to |dst| of - * length |dstcnt|. The total number of bytes which the copied - * ngtcp2_vec refers to is at most |left| and is assigned to - * |*pnwritten|. The empty elements in |src| are ignored. This - * function returns the number of elements copied. - */ -size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, - size_t dstcnt, const ngtcp2_vec *src, - size_t srccnt, size_t left); - -/* - * ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must - * have sufficient capacity. - */ -void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt); - -#endif /* NGTCP2_VEC_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_version.c b/deps/ngtcp2/lib/ngtcp2_version.c deleted file mode 100644 index 40f3ae3f9ea..00000000000 --- a/deps/ngtcp2/lib/ngtcp2_version.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2019 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 <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <ngtcp2/ngtcp2.h> - -static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM, - NGTCP2_VERSION}; - -ngtcp2_info *ngtcp2_version(int least_version) { - if (least_version > NGTCP2_VERSION_NUM) { - return NULL; - } - return &version; -} diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp deleted file mode 100644 index fbdd5568955..00000000000 --- a/deps/ngtcp2/ngtcp2.gyp +++ /dev/null @@ -1,118 +0,0 @@ -{ - 'target_defaults': { - 'defines': [ - '_U_=' - ] - }, - 'targets': [ - { - 'target_name': 'ngtcp2', - 'type': 'static_library', - 'include_dirs': [ - 'lib/includes', - 'lib', - 'crypto/includes', - 'crypto' - ], - 'defines': [ - 'BUILDING_NGTCP2', - 'NGTCP2_STATICLIB', - ], - 'dependencies': [ - '../openssl/openssl.gyp:openssl' - ], - 'conditions': [ - ['OS=="win"', { - 'defines': [ - 'WIN32', - '_WINDOWS', - 'HAVE_CONFIG_H', - ], - 'msvs_settings': { - 'VCCLCompilerTool': { - 'CompileAs': '1' - }, - }, - }], - ['OS=="linux"', { - 'defines': [ - 'HAVE_ARPA_INET_H', - 'HAVE_NETINET_IN_H', - ], - }], - ], - 'direct_dependent_settings': { - 'defines': [ 'NGTCP2_STATICLIB' ], - 'include_dirs': [ - 'lib/includes', - 'crypto/includes', - ] - }, - 'sources': [ - 'lib/ngtcp2_acktr.c', - 'lib/ngtcp2_acktr.h', - 'lib/ngtcp2_addr.c', - 'lib/ngtcp2_addr.h', - 'lib/ngtcp2_buf.c', - 'lib/ngtcp2_buf.h', - 'lib/ngtcp2_cc.c', - 'lib/ngtcp2_cc.h', - 'lib/ngtcp2_cid.c', - 'lib/ngtcp2_cid.h', - 'lib/ngtcp2_conn.c', - 'lib/ngtcp2_conn.h', - 'lib/ngtcp2_conv.c', - 'lib/ngtcp2_conv.h', - 'lib/ngtcp2_crypto.c', - 'lib/ngtcp2_crypto.h', - 'lib/ngtcp2_err.c', - 'lib/ngtcp2_err.h', - 'lib/ngtcp2_gaptr.c', - 'lib/ngtcp2_gaptr.h', - 'lib/ngtcp2_idtr.c', - 'lib/ngtcp2_idtr.h', - 'lib/ngtcp2_ksl.c', - 'lib/ngtcp2_ksl.h', - 'lib/ngtcp2_log.c', - 'lib/ngtcp2_log.h', - 'lib/ngtcp2_macro.h', - 'lib/ngtcp2_map.c', - 'lib/ngtcp2_map.h', - 'lib/ngtcp2_mem.c', - 'lib/ngtcp2_mem.h', - 'lib/ngtcp2_path.c', - 'lib/ngtcp2_path.h', - 'lib/ngtcp2_pkt.c', - 'lib/ngtcp2_pkt.h', - 'lib/ngtcp2_ppe.c', - 'lib/ngtcp2_ppe.h', - 'lib/ngtcp2_pq.c', - 'lib/ngtcp2_pq.h', - 'lib/ngtcp2_pv.c', - 'lib/ngtcp2_pv.h', - 'lib/ngtcp2_qlog.c', - 'lib/ngtcp2_qlog.h', - 'lib/ngtcp2_range.c', - 'lib/ngtcp2_range.h', - 'lib/ngtcp2_ringbuf.c', - 'lib/ngtcp2_ringbuf.h', - 'lib/ngtcp2_rob.c', - 'lib/ngtcp2_rob.h', - 'lib/ngtcp2_rtb.c', - 'lib/ngtcp2_rtb.h', - 'lib/ngtcp2_rst.c', - 'lib/ngtcp2_rst.h', - 'lib/ngtcp2_str.c', - 'lib/ngtcp2_str.h', - 'lib/ngtcp2_strm.c', - 'lib/ngtcp2_strm.h', - 'lib/ngtcp2_vec.c', - 'lib/ngtcp2_vec.h', - 'lib/ngtcp2_version.c', - 'crypto/shared.c', - 'crypto/shared.h', - 'crypto/openssl/openssl.c' - ] - } - ] -} diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index 3dfe0b8c8a0..4609d83baab 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -16,13 +16,6 @@ 'OPENSSL_NO_HW', ], 'conditions': [ - [ - # Disable building QUIC support in openssl if experimental_quic - # is not enabled. - 'experimental_quic!=1', { - 'defines': ['OPENSSL_NO_QUIC=1'], - } - ], [ 'openssl_no_asm==1', { 'includes': ['./openssl_no_asm.gypi'], }, 'target_arch=="arm64" and OS=="win"', { diff --git a/deps/openssl/patches/0001-deps-add-support-for-BoringSSL-QUIC-APIs.patch b/deps/openssl/patches/0001-deps-add-support-for-BoringSSL-QUIC-APIs.patch deleted file mode 100644 index e526302fa1a..00000000000 --- a/deps/openssl/patches/0001-deps-add-support-for-BoringSSL-QUIC-APIs.patch +++ /dev/null @@ -1,2363 +0,0 @@ -From d82945ead1320ea893c237e1c0f37c4afd933eed Mon Sep 17 00:00:00 2001 -From: Todd Short <tshort@akamai.com> -Date: Fri, 12 Apr 2019 11:13:25 -0400 -Subject: [PATCH 1/3] deps: add support for BoringSSL QUIC APIs - -Acquired from: https://github.com/akamai/openssl/tree/OpenSSL_1_1_1f-quic - -Squashed: - -* -https://github.com/akamai/openssl/commit/2ef7c58b2cb432abd4e371322667228d7ce2637b -* -https://github.com/akamai/openssl/commit/3f8eda3128f52f4d14399a1a912d1fdfacd86a86 -* -https://github.com/akamai/openssl/commit/b37f665884be2e17e8ff4ad919138626fb13f6c9 -* -https://github.com/akamai/openssl/commit/6b235895a16da3c0dd36a24cf8dfbe249c6cda3c -* -https://github.com/akamai/openssl/commit/3a793e06a5031311ce7ce094455be87fa92b8240 - ---- - -This is a cherry-pick of 2a4b03a306439307e0b822b17eda3bdabddfbb68 -on the master-quic-support2 branch (2019-10-07) -Which was a rebase/squash of master-quic-support: - -* 5aa62ce Add support for more secrets - Todd Short/Todd Short (master-quic-support) -* 58e0643 Tweeks to quic_change_cipher_state() - Todd Short/Todd Short -* 8169702 Move QUIC code out of tls13_change_cipher_state() - Todd Short/Todd Short -* a08cfe6 Correctly disable middlebox compat - Todd Short/Todd Short -* 3a9eabf Add OPENSSL_NO_QUIC wrapper - Todd Short/Todd Short -* f550eca Add client early traffic secret storage - Todd Short/Todd Short -* 1b787ae Quick fix: s2c to c2s for early secret - Todd Short/Todd Short -* f97e6a9 Don't process an incomplete message - Todd Short/Todd Short -* 81f0ce2 Reset init state in SSL_process_quic_post_handshake() - Todd Short/Todd Short -* 5d59cf9 Fix quic_transport constructors/parsers - Todd Short/Todd Short -* 5e5f91c Fix INSTALL nit. - Todd Short/Todd Short -* bd290ab Fix duplicate word in docs - Todd Short/Todd Short -* 699590b fixup! Handle partial handshake messages - Todd Short/Todd Short -* a472a8d Handle partial handshake messages - Todd Short/Todd Short -* 363cf3d fixup! Use proper secrets for handshake - Todd Short/Todd Short -* b03fee6 Use proper secrets for handshake - Todd Short/Todd Short -* 2ab1aa0 Move QUIC transport params to encrypted extensions - Todd Short/Todd Short -* 0d16af9 Make temp secret names less confusing - Todd Short/Todd Short -* abb6f39 New method to get QUIC secret length - Todd Short/Todd Short -* 05fdae9 Add support for BoringSSL QUIC APIs - Todd Short/Todd Short - -This adds a compatible API for BoringSSL's QUIC support, based -on the current |draft-ietf-quic-tls|. - -Based on BoringSSL commit 3c034b2cf386b3131f75520705491871a2e0cafe -Based on BoringSSL commit c8e0f90f83b9ec38ea833deb86b5a41360b62b6a -Based on BoringSSL commit 3cbb0299a28a8bd0136257251a78b91a96c5eec8 -Based on BoringSSL commit cc9d935256539af2d3b7f831abf57c0d685ffd81 -Based on BoringSSL commit e6eef1ca16a022e476bbaedffef044597cfc8f4b -Based on BoringSSL commit 6f733791148cf8a076bf0e95498235aadbe5926d -Based on BoringSSL commit 384d0eaf1930af1ebc47eda751f0c78dfcba1c03 -Based on BoringSSL commit a0373182eb5cc7b81d49f434596b473c7801c942 -Based on BoringSSL commit b1b76aee3cb43ce11889403c5334283d951ebd37 - -New method to get QUIC secret length - -Make temp secret names less confusing - -Move QUIC transport params to encrypted extensions - -Use proper secrets for handshake - -fixup! Use proper secrets for handshake - -Handle partial handshake messages - -fixup! Handle partial handshake messages - -Fix duplicate word in docs - -Fix INSTALL nit. - -Fix quic_transport constructors/parsers - -Reset init state in SSL_process_quic_post_handshake() - -Don't process an incomplete message - -Quick fix: s2c to c2s for early secret - -Add client early traffic secret storage - -Add OPENSSL_NO_QUIC wrapper - -Correctly disable middlebox compat - -Move QUIC code out of tls13_change_cipher_state() - -Create quic_change_cipher_state() that does the minimal required -to generate the QUIC secrets. (e.g. encryption contexts are not -initialized). - -Tweeks to quic_change_cipher_state() - -Add support for more secrets - -Fix resumption secret - -(cherry picked from commit 16fafdf4e0ec6cddd5705f407e5dca26cb30914d) - -QUIC: Handle EndOfEarlyData and MaxEarlyData - -QUIC: Increase HKDF_MAXBUF to 2048 - -Fall-through for 0RTT - -Signed-off-by: James M Snell <jasnell@gmail.com> ---- - deps/openssl/openssl/CHANGES | 3 + - deps/openssl/openssl/Configure | 3 + - deps/openssl/openssl/INSTALL | 3 + - deps/openssl/openssl/crypto/err/openssl.txt | 20 +- - deps/openssl/openssl/crypto/kdf/hkdf.c | 2 +- - .../openssl/doc/man3/SSL_CIPHER_get_name.pod | 13 + - .../doc/man3/SSL_CTX_set_quic_method.pod | 232 ++++++++++++++ - deps/openssl/openssl/include/openssl/evp.h | 4 + - .../openssl/include/openssl/ossl_typ.h | 2 + - deps/openssl/openssl/include/openssl/ssl.h | 45 +++ - deps/openssl/openssl/include/openssl/sslerr.h | 18 +- - deps/openssl/openssl/include/openssl/tls1.h | 3 + - deps/openssl/openssl/ssl/build.info | 3 +- - deps/openssl/openssl/ssl/s3_msg.c | 12 +- - deps/openssl/openssl/ssl/ssl_ciph.c | 32 ++ - deps/openssl/openssl/ssl/ssl_err.c | 26 ++ - deps/openssl/openssl/ssl/ssl_lib.c | 41 ++- - deps/openssl/openssl/ssl/ssl_local.h | 44 +++ - deps/openssl/openssl/ssl/ssl_quic.c | 285 ++++++++++++++++++ - deps/openssl/openssl/ssl/statem/extensions.c | 29 ++ - .../openssl/ssl/statem/extensions_clnt.c | 52 ++++ - .../openssl/ssl/statem/extensions_srvr.c | 55 +++- - deps/openssl/openssl/ssl/statem/statem.c | 21 +- - deps/openssl/openssl/ssl/statem/statem_clnt.c | 8 + - deps/openssl/openssl/ssl/statem/statem_lib.c | 19 +- - .../openssl/openssl/ssl/statem/statem_local.h | 19 ++ - deps/openssl/openssl/ssl/statem/statem_quic.c | 109 +++++++ - deps/openssl/openssl/ssl/statem/statem_srvr.c | 3 +- - deps/openssl/openssl/ssl/tls13_enc.c | 155 ++++++++-- - deps/openssl/openssl/test/sslapitest.c | 132 ++++++++ - deps/openssl/openssl/test/ssltestlib.c | 5 + - deps/openssl/openssl/test/tls13secretstest.c | 7 + - deps/openssl/openssl/util/libssl.num | 11 + - deps/openssl/openssl/util/private.num | 2 + - 34 files changed, 1383 insertions(+), 35 deletions(-) - create mode 100644 deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod - create mode 100644 deps/openssl/openssl/ssl/ssl_quic.c - create mode 100644 deps/openssl/openssl/ssl/statem/statem_quic.c - -diff --git a/deps/openssl/openssl/CHANGES b/deps/openssl/openssl/CHANGES -index 057405b0bf..f660c799f0 100644 ---- a/deps/openssl/openssl/CHANGES -+++ b/deps/openssl/openssl/CHANGES -@@ -115,6 +115,9 @@ - - Changes between 1.1.1c and 1.1.1d [10 Sep 2019] - -+ *) Implement BoringSSL's QUIC API -+ [Todd Short] -+ - *) Fixed a fork protection issue. OpenSSL 1.1.1 introduced a rewritten random - number generator (RNG). This was intended to include protection in the - event of a fork() system call in order to ensure that the parent and child -diff --git a/deps/openssl/openssl/Configure b/deps/openssl/openssl/Configure -index 2e9efaa5f3..79a60d0cb5 100755 ---- a/deps/openssl/openssl/Configure -+++ b/deps/openssl/openssl/Configure -@@ -391,6 +391,7 @@ my @disablables = ( - "poly1305", - "posix-io", - "psk", -+ "quic", - "rc2", - "rc4", - "rc5", -@@ -507,6 +508,8 @@ my @disable_cascades = ( - sub { !$disabled{"unit-test"} } => [ "heartbeats" ], - - sub { !$disabled{"msan"} } => [ "asm" ], -+ -+ "tls1_3" => [ "quic" ], - ); - - # Avoid protocol support holes. Also disable all versions below N, if version -diff --git a/deps/openssl/openssl/INSTALL b/deps/openssl/openssl/INSTALL -index f5118428b3..5938e185a1 100644 ---- a/deps/openssl/openssl/INSTALL -+++ b/deps/openssl/openssl/INSTALL -@@ -456,6 +456,9 @@ - no-psk - Don't build support for Pre-Shared Key based ciphersuites. - -+ no-quic -+ Don't build with support for QUIC. -+ - no-rdrand - Don't use hardware RDRAND capabilities. - -diff --git a/deps/openssl/openssl/crypto/err/openssl.txt b/deps/openssl/openssl/crypto/err/openssl.txt -index 35512f9caf..e7b8799070 100644 ---- a/deps/openssl/openssl/crypto/err/openssl.txt -+++ b/deps/openssl/openssl/crypto/err/openssl.txt -@@ -1180,7 +1180,7 @@ SSL_F_OSSL_STATEM_SERVER_CONSTRUCT_MESSAGE:431:* - SSL_F_OSSL_STATEM_SERVER_POST_PROCESS_MESSAGE:601:\ - ossl_statem_server_post_process_message - SSL_F_OSSL_STATEM_SERVER_POST_WORK:602:ossl_statem_server_post_work --SSL_F_OSSL_STATEM_SERVER_PRE_WORK:640: -+SSL_F_OSSL_STATEM_SERVER_PRE_WORK:640:ossl_statem_server_pre_work - SSL_F_OSSL_STATEM_SERVER_PROCESS_MESSAGE:603:ossl_statem_server_process_message - SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION:418:ossl_statem_server_read_transition - SSL_F_OSSL_STATEM_SERVER_WRITE_TRANSITION:604:\ -@@ -1189,6 +1189,9 @@ SSL_F_PARSE_CA_NAMES:541:parse_ca_names - SSL_F_PITEM_NEW:624:pitem_new - SSL_F_PQUEUE_NEW:625:pqueue_new - SSL_F_PROCESS_KEY_SHARE_EXT:439:* -+SSL_F_QUIC_CHANGE_CIPHER_STATE:639:quic_change_cipher_state -+SSL_F_QUIC_GET_MESSAGE:641:quic_get_message -+SSL_F_QUIC_SET_ENCRYPTION_SECRETS:642:quic_set_encryption_secrets - SSL_F_READ_STATE_MACHINE:352:read_state_machine - SSL_F_SET_CLIENT_CIPHERSUITE:540:set_client_ciphersuite - SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET:595:srp_generate_client_master_secret -@@ -1199,7 +1202,9 @@ SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM:130:ssl3_check_cert_and_algorithm - SSL_F_SSL3_CTRL:213:ssl3_ctrl - SSL_F_SSL3_CTX_CTRL:133:ssl3_ctx_ctrl - SSL_F_SSL3_DIGEST_CACHED_RECORDS:293:ssl3_digest_cached_records -+SSL_F_SSL3_DISPATCH_ALERT:643:ssl3_dispatch_alert - SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC:292:ssl3_do_change_cipher_spec -+SSL_F_SSL3_DO_WRITE:644:ssl3_do_write - SSL_F_SSL3_ENC:608:ssl3_enc - SSL_F_SSL3_FINAL_FINISH_MAC:285:ssl3_final_finish_mac - SSL_F_SSL3_FINISH_MAC:587:ssl3_finish_mac -@@ -1307,6 +1312,8 @@ SSL_F_SSL_PARSE_SERVERHELLO_USE_SRTP_EXT:311:* - SSL_F_SSL_PEEK:270:SSL_peek - SSL_F_SSL_PEEK_EX:432:SSL_peek_ex - SSL_F_SSL_PEEK_INTERNAL:522:ssl_peek_internal -+SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE:645:SSL_process_quic_post_handshake -+SSL_F_SSL_PROVIDE_QUIC_DATA:646:SSL_provide_quic_data - SSL_F_SSL_READ:223:SSL_read - SSL_F_SSL_READ_EARLY_DATA:529:SSL_read_early_data - SSL_F_SSL_READ_EX:434:SSL_read_ex -@@ -1356,6 +1363,7 @@ SSL_F_SSL_WRITE_EARLY_DATA:526:SSL_write_early_data - SSL_F_SSL_WRITE_EARLY_FINISH:527:* - SSL_F_SSL_WRITE_EX:433:SSL_write_ex - SSL_F_SSL_WRITE_INTERNAL:524:ssl_write_internal -+SSL_F_STATEM_FLUSH:647:statem_flush - SSL_F_STATE_MACHINE:353:state_machine - SSL_F_TLS12_CHECK_PEER_SIGALG:333:tls12_check_peer_sigalg - SSL_F_TLS12_COPY_SIGALGS:533:tls12_copy_sigalgs -@@ -1419,6 +1427,8 @@ SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH:619:\ - tls_construct_ctos_post_handshake_auth - SSL_F_TLS_CONSTRUCT_CTOS_PSK:501:tls_construct_ctos_psk - SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES:509:tls_construct_ctos_psk_kex_modes -+SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS:648:\ -+ tls_construct_ctos_quic_transport_params - SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE:473:tls_construct_ctos_renegotiate - SSL_F_TLS_CONSTRUCT_CTOS_SCT:474:tls_construct_ctos_sct - SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME:475:tls_construct_ctos_server_name -@@ -1460,6 +1470,8 @@ SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE:456:tls_construct_stoc_key_share - SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN:548:tls_construct_stoc_maxfragmentlen - SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG:457:tls_construct_stoc_next_proto_neg - SSL_F_TLS_CONSTRUCT_STOC_PSK:504:tls_construct_stoc_psk -+SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS:649:\ -+ tls_construct_stoc_quic_transport_params - SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE:458:tls_construct_stoc_renegotiate - SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME:459:tls_construct_stoc_server_name - SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET:460:tls_construct_stoc_session_ticket -@@ -1488,6 +1500,8 @@ SSL_F_TLS_PARSE_CTOS_MAXFRAGMENTLEN:571:tls_parse_ctos_maxfragmentlen - SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH:620:tls_parse_ctos_post_handshake_auth - SSL_F_TLS_PARSE_CTOS_PSK:505:tls_parse_ctos_psk - SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES:572:tls_parse_ctos_psk_kex_modes -+SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS:650:\ -+ tls_parse_ctos_quic_transport_params - SSL_F_TLS_PARSE_CTOS_RENEGOTIATE:464:tls_parse_ctos_renegotiate - SSL_F_TLS_PARSE_CTOS_SERVER_NAME:573:tls_parse_ctos_server_name - SSL_F_TLS_PARSE_CTOS_SESSION_TICKET:574:tls_parse_ctos_session_ticket -@@ -1506,6 +1520,8 @@ SSL_F_TLS_PARSE_STOC_KEY_SHARE:445:tls_parse_stoc_key_share - SSL_F_TLS_PARSE_STOC_MAXFRAGMENTLEN:581:tls_parse_stoc_maxfragmentlen - SSL_F_TLS_PARSE_STOC_NPN:582:tls_parse_stoc_npn - SSL_F_TLS_PARSE_STOC_PSK:502:tls_parse_stoc_psk -+SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS:651:\ -+ tls_parse_stoc_quic_transport_params - SSL_F_TLS_PARSE_STOC_RENEGOTIATE:448:tls_parse_stoc_renegotiate - SSL_F_TLS_PARSE_STOC_SCT:564:tls_parse_stoc_sct - SSL_F_TLS_PARSE_STOC_SERVER_NAME:583:tls_parse_stoc_server_name -@@ -2706,6 +2722,7 @@ SSL_R_INCONSISTENT_EARLY_DATA_ALPN:222:inconsistent early data alpn - SSL_R_INCONSISTENT_EARLY_DATA_SNI:231:inconsistent early data sni - SSL_R_INCONSISTENT_EXTMS:104:inconsistent extms - SSL_R_INSUFFICIENT_SECURITY:241:insufficient security -+SSL_R_INTERNAL_ERROR:295:internal error - SSL_R_INVALID_ALERT:205:invalid alert - SSL_R_INVALID_CCS_MESSAGE:260:invalid ccs message - SSL_R_INVALID_CERTIFICATE_OR_ALG:238:invalid certificate or alg -@@ -2881,6 +2898,7 @@ SSL_R_VERSION_TOO_LOW:396:version too low - SSL_R_WRONG_CERTIFICATE_TYPE:383:wrong certificate type - SSL_R_WRONG_CIPHER_RETURNED:261:wrong cipher returned - SSL_R_WRONG_CURVE:378:wrong curve -+SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED:296:wrong encryption level received - SSL_R_WRONG_SIGNATURE_LENGTH:264:wrong signature length - SSL_R_WRONG_SIGNATURE_SIZE:265:wrong signature size - SSL_R_WRONG_SIGNATURE_TYPE:370:wrong signature type -diff --git a/deps/openssl/openssl/crypto/kdf/hkdf.c b/deps/openssl/openssl/crypto/kdf/hkdf.c -index 25bf4b729f..6d1a32c885 100644 ---- a/deps/openssl/openssl/crypto/kdf/hkdf.c -+++ b/deps/openssl/openssl/crypto/kdf/hkdf.c -@@ -15,7 +15,7 @@ - #include "internal/cryptlib.h" - #include "crypto/evp.h" - --#define HKDF_MAXBUF 1024 -+#define HKDF_MAXBUF 2048 - - static unsigned char *HKDF(const EVP_MD *evp_md, - const unsigned char *salt, size_t salt_len, -diff --git a/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod b/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod -index 26edae3d80..20437b76e8 100644 ---- a/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod -+++ b/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod -@@ -13,6 +13,7 @@ SSL_CIPHER_get_digest_nid, - SSL_CIPHER_get_handshake_digest, - SSL_CIPHER_get_kx_nid, - SSL_CIPHER_get_auth_nid, -+SSL_CIPHER_get_prf_nid, - SSL_CIPHER_is_aead, - SSL_CIPHER_find, - SSL_CIPHER_get_id, -@@ -34,6 +35,7 @@ SSL_CIPHER_get_protocol_id - const EVP_MD *SSL_CIPHER_get_handshake_digest(const SSL_CIPHER *c); - int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c); - int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c); -+ int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); - int SSL_CIPHER_is_aead(const SSL_CIPHER *c); - const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr); - uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *c); -@@ -91,6 +93,15 @@ TLS 1.3 cipher suites) B<NID_auth_any> is returned. Examples (not comprehensive) - NID_auth_ecdsa - NID_auth_psk - -+SSL_CIPHER_get_prf_nid() retuns the pseudo-random function NID for B<c>. If B<c> is -+a pre-TLS-1.2 cipher, it returns B<NID_md5_sha1> but note these ciphers use -+SHA-256 in TLS 1.2. Other return values may be treated uniformly in all -+applicable versions. Examples (not comprehensive): -+ -+ NID_md5_sha1 -+ NID_sha256 -+ NID_sha384 -+ - SSL_CIPHER_is_aead() returns 1 if the cipher B<c> is AEAD (e.g. GCM or - ChaCha20/Poly1305), and 0 if it is not AEAD. - -@@ -201,6 +212,8 @@ required to enable this function. - - The OPENSSL_cipher_name() function was added in OpenSSL 1.1.1. - -+The SSL_CIPHER_get_prf_nid() function was added in OpenSSL 3.0.0. -+ - =head1 COPYRIGHT - - Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved. -diff --git a/deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod b/deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod -new file mode 100644 -index 0000000000..60bf704944 ---- /dev/null -+++ b/deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod -@@ -0,0 +1,232 @@ -+=pod -+ -+=head1 NAME -+ -+SSL_QUIC_METHOD, -+OSSL_ENCRYPTION_LEVEL, -+SSL_CTX_set_quic_method, -+SSL_set_quic_method, -+SSL_set_quic_transport_params, -+SSL_get_peer_quic_transport_params, -+SSL_quic_max_handshake_flight_len, -+SSL_quic_read_level, -+SSL_quic_write_level, -+SSL_provide_quic_data, -+SSL_process_quic_post_handshake, -+SSL_is_quic -+- QUIC support -+ -+=head1 SYNOPSIS -+ -+ #include <openssl/ssl.h> -+ -+ typedef struct ssl_quic_method_st SSL_QUIC_METHOD; -+ typedef enum ssl_encryption_level_t OSSL_ENCRYPTION_LEVEL; -+ -+ int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method); -+ int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method); -+ int SSL_set_quic_transport_params(SSL *ssl, -+ const uint8_t *params, -+ size_t params_len); -+ void SSL_get_peer_quic_transport_params(const SSL *ssl, -+ const uint8_t **out_params, -+ size_t *out_params_len); -+ size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level); -+ OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl); -+ OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl); -+ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *data, size_t len); -+ int SSL_process_quic_post_handshake(SSL *ssl); -+ int SSL_is_quic(SSL *ssl); -+ -+=head1 DESCRIPTION -+ -+SSL_CTX_set_quic_method() and SSL_set_quic_method() configures the QUIC methods. -+This should only be configured with a minimum version of TLS 1.3. B<quic_method> -+must remain valid for the lifetime of B<ctx> or B<ssl>. Calling this disables -+the SSL_OP_ENABLE_MIDDLEBOX_COMPAT option, which is not required for QUIC. -+ -+SSL_set_quic_transport_params() configures B<ssl> to send B<params> (of length -+B<params_len>) in the quic_transport_parameters extension in either the -+ClientHello or EncryptedExtensions handshake message. This extension will -+only be sent if the TLS version is at least 1.3, and for a server, only if -+the client sent the extension. The buffer pointed to by B<params> only need be -+valid for the duration of the call to this function. -+ -+SSL_get_peer_quic_transport_params() provides the caller with the value of the -+quic_transport_parameters extension sent by the peer. A pointer to the buffer -+containing the TransportParameters will be put in B<*out_params>, and its -+length in B<*out_params_len>. This buffer will be valid for the lifetime of the -+B<ssl>. If no params were received from the peer, B<*out_params_len> will be 0. -+ -+SSL_quic_max_handshake_flight_len() returns the maximum number of bytes -+that may be received at the given encryption level. This function should be -+used to limit buffering in the QUIC implementation. -+ -+See https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-4.4. -+ -+SSL_quic_read_level() returns the current read encryption level. -+ -+SSL_quic_write_level() returns the current write encryption level. -+ -+SSL_provide_quic_data() provides data from QUIC at a particular encryption -+level B<level>. It is an error to call this function outside of the handshake -+or with an encryption level other than the current read level. It returns one -+on success and zero on error. -+ -+SSL_process_quic_post_handshake() processes any data that QUIC has provided -+after the handshake has completed. This includes NewSessionTicket messages -+sent by the server. -+ -+SSL_is_quic() indicates whether a connection uses QUIC. -+ -+=head1 NOTES -+ -+These APIs are implementations of BoringSSL's QUIC APIs. -+ -+QUIC acts as an underlying transport for the TLS 1.3 handshake. The following -+functions allow a QUIC implementation to serve as the underlying transport as -+described in draft-ietf-quic-tls. -+ -+When configured for QUIC, SSL_do_handshake() will drive the handshake as -+before, but it will not use the configured B<BIO>. It will call functions on -+B<SSL_QUIC_METHOD> to configure secrets and send data. If data is needed from -+the peer, it will return B<SSL_ERROR_WANT_READ>. When received, the caller -+should call SSL_provide_quic_data() and then SSL_do_handshake() to continue -+the handshake. After the handshake is complete, the caller should call -+SSL_provide_quic_data() for any post-handshake data, followed by -+SSL_process_quic_post_handshake() to process it. It is an error to call -+SSL_read()/SSL_read_ex() and SSL_write()/SSL_write_ex() in QUIC. -+ -+Note that secrets for an encryption level may be available to QUIC before the -+level is active in TLS. Callers should use SSL_quic_read_level() to determine -+the active read level for SSL_provide_quic_data(). SSL_do_handshake() will -+pass the active write level to add_handshake_data() when writing data. Callers -+can use SSL_quic_write_level() to query the active write level when -+generating their own errors. -+ -+See https://tools.ietf.org/html/draft-ietf-quic-tls-15#section-4.1 for more -+details. -+ -+To avoid DoS attacks, the QUIC implementation must limit the amount of data -+being queued up. The implementation can call -+SSL_quic_max_handshake_flight_len() to get the maximum buffer length at each -+encryption level. -+ -+draft-ietf-quic-tls defines a new TLS extension quic_transport_parameters -+used by QUIC for each endpoint to unilaterally declare its supported -+transport parameters. draft-ietf-quic-transport (section 7.4) defines the -+contents of that extension (a TransportParameters struct) and describes how -+to handle it and its semantic meaning. -+ -+OpenSSL handles this extension as an opaque byte string. The caller is -+responsible for serializing and parsing it. -+ -+=head2 OSSL_ENCRYPTION_LEVEL -+ -+B<OSSL_ENCRYPTION_LEVEL> (B<enum ssl_encryption_level_t>) represents the -+encryption levels: -+ -+=over 4 -+ -+=item ssl_encryption_initial -+ -+The initial encryption level that is used for client and server hellos. -+ -+=item ssl_encryption_early_data -+ -+The encryption level for early data. This is a write-level for the client -+and a read-level for the server. -+ -+=item ssl_encryption_handshake -+ -+The encryption level for the remainder of the handshake. -+ -+=item ssl_encryption_application -+ -+The encryption level for the application data. -+ -+=back -+ -+=head2 SSL_QUIC_METHOD -+ -+The B<SSL_QUIC_METHOD> (B<struct ssl_quic_method_st>) describes the -+QUIC methods. -+ -+ struct ssl_quic_method_st { -+ int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *read_secret, -+ const uint8_t *write_secret, size_t secret_len); -+ int (*add_handshake_data)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *data, size_t len); -+ int (*flush_flight)(SSL *ssl); -+ int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert); -+ }; -+ typedef struct ssl_quic_method_st SSL_QUIC_METHOD; -+ -+set_encryption_secrets() configures the read and write secrets for the given -+encryption level. This function will always be called before an encryption -+level other than B<ssl_encryption_initial> is used. Note, however, that -+secrets for a level may be configured before TLS is ready to send or accept -+data at that level. -+ -+When reading packets at a given level, the QUIC implementation must send -+ACKs at the same level, so this function provides read and write secrets -+together. The exception is B<ssl_encryption_early_data>, where secrets are -+only available in the client to server direction. The other secret will be -+NULL. The server acknowledges such data at B<ssl_encryption_application>, -+which will be configured in the same SSL_do_handshake() call. -+ -+This function should use SSL_get_current_cipher() to determine the TLS -+cipher suite. -+ -+add_handshake_data() adds handshake data to the current flight at the given -+encryption level. It returns one on success and zero on error. -+ -+OpenSSL will pack data from a single encryption level together, but a -+single handshake flight may include multiple encryption levels. Callers -+should defer writing data to the network until flush_flight() to better -+pack QUIC packets into transport datagrams. -+ -+flush_flight() is called when the current flight is complete and should be -+written to the transport. Note a flight may contain data at several -+encryption levels. -+ -+send_alert() sends a fatal alert at the specified encryption level. -+ -+All QUIC methods return 1 on success and 0 on error. -+ -+=head1 RETURN VALUES -+ -+SSL_CTX_set_quic_method(), -+SSL_set_quic_method(), -+SSL_set_quic_transport_params(), and -+SSL_process_quic_post_handshake() -+return 1 on success, and 0 on error. -+ -+SSL_quic_read_level() and SSL_quic_write_level() return the current -+encryption level as B<OSSL_ENCRYPTION_LEVEL> (B<enum ssl_encryption_level_t>). -+ -+SSL_quic_max_handshake_flight_len() returns the maximum length of a flight -+for a given encryption level. -+ -+SSL_is_quic() returns 1 if QUIC is being used, 0 if not. -+ -+=head1 SEE ALSO -+ -+L<ssl(7)>, L<SSL_CIPHER_get_prf_nid(3)>, L<SSL_do_handshake(3)> -+ -+=head1 HISTORY -+ -+These functions were added in OpenSSL 3.0.0. -+ -+=head1 COPYRIGHT -+ -+Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. -+ -+Licensed under the Apache License 2.0 (the "License"). You may not use -+this file except in compliance with the License. You can obtain a copy -+in the file LICENSE in the source distribution or at -+L<https://www.openssl.org/source/license.html>. -+ -+=cut -diff --git a/deps/openssl/openssl/include/openssl/evp.h b/deps/openssl/openssl/include/openssl/evp.h -index a411f3f2f9..275b7a4acc 100644 ---- a/deps/openssl/openssl/include/openssl/evp.h -+++ b/deps/openssl/openssl/include/openssl/evp.h -@@ -1324,6 +1324,10 @@ void EVP_PKEY_asn1_set_security_bits(EVP_PKEY_ASN1_METHOD *ameth, - */ - # define EVP_PKEY_FLAG_SIGCTX_CUSTOM 4 - -+/* Used by Chromium/QUIC */ -+# define X25519_PRIVATE_KEY_LEN 32 -+# define X25519_PUBLIC_VALUE_LEN 32 -+ - const EVP_PKEY_METHOD *EVP_PKEY_meth_find(int type); - EVP_PKEY_METHOD *EVP_PKEY_meth_new(int id, int flags); - void EVP_PKEY_meth_get0_info(int *ppkey_id, int *pflags, -diff --git a/deps/openssl/openssl/include/openssl/ossl_typ.h b/deps/openssl/openssl/include/openssl/ossl_typ.h -index e0edfaaf47..d2fdce8fdf 100644 ---- a/deps/openssl/openssl/include/openssl/ossl_typ.h -+++ b/deps/openssl/openssl/include/openssl/ossl_typ.h -@@ -176,6 +176,8 @@ typedef struct ct_policy_eval_ctx_st CT_POLICY_EVAL_CTX; - typedef struct ossl_store_info_st OSSL_STORE_INFO; - typedef struct ossl_store_search_st OSSL_STORE_SEARCH; - -+typedef struct ssl_quic_method_st SSL_QUIC_METHOD; -+ - #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \ - defined(INTMAX_MAX) && defined(UINTMAX_MAX) - typedef intmax_t ossl_intmax_t; -diff --git a/deps/openssl/openssl/include/openssl/ssl.h b/deps/openssl/openssl/include/openssl/ssl.h -index 6724ccf2d2..f21458cd5e 100644 ---- a/deps/openssl/openssl/include/openssl/ssl.h -+++ b/deps/openssl/openssl/include/openssl/ssl.h -@@ -2432,6 +2432,51 @@ void SSL_set_allow_early_data_cb(SSL *s, - SSL_allow_early_data_cb_fn cb, - void *arg); - -+# ifndef OPENSSL_NO_QUIC -+/* -+ * QUIC integration - The QUIC interface matches BoringSSL -+ * -+ * ssl_encryption_level_t represents a specific QUIC encryption level used to -+ * transmit handshake messages. BoringSSL has this as an 'enum'. -+ */ -+typedef enum ssl_encryption_level_t { -+ ssl_encryption_initial = 0, -+ ssl_encryption_early_data, -+ ssl_encryption_handshake, -+ ssl_encryption_application -+} OSSL_ENCRYPTION_LEVEL; -+ -+struct ssl_quic_method_st { -+ int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *read_secret, -+ const uint8_t *write_secret, size_t secret_len); -+ int (*add_handshake_data)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *data, size_t len); -+ int (*flush_flight)(SSL *ssl); -+ int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert); -+}; -+ -+__owur int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method); -+__owur int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method); -+__owur int SSL_set_quic_transport_params(SSL *ssl, -+ const uint8_t *params, -+ size_t params_len); -+void SSL_get_peer_quic_transport_params(const SSL *ssl, -+ const uint8_t **out_params, -+ size_t *out_params_len); -+__owur size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level); -+__owur OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl); -+__owur OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl); -+__owur int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *data, size_t len); -+__owur int SSL_process_quic_post_handshake(SSL *ssl); -+ -+__owur int SSL_is_quic(SSL *ssl); -+ -+# endif -+ -+int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); -+ - # ifdef __cplusplus - } - # endif -diff --git a/deps/openssl/openssl/include/openssl/sslerr.h b/deps/openssl/openssl/include/openssl/sslerr.h -index 82983d3c1e..e3915c0a55 100644 ---- a/deps/openssl/openssl/include/openssl/sslerr.h -+++ b/deps/openssl/openssl/include/openssl/sslerr.h -@@ -11,9 +11,7 @@ - #ifndef HEADER_SSLERR_H - # define HEADER_SSLERR_H - --# ifndef HEADER_SYMHACKS_H --# include <openssl/symhacks.h> --# endif -+# include <openssl/symhacks.h> - - # ifdef __cplusplus - extern "C" -@@ -96,6 +94,9 @@ int ERR_load_SSL_strings(void); - # define SSL_F_PITEM_NEW 624 - # define SSL_F_PQUEUE_NEW 625 - # define SSL_F_PROCESS_KEY_SHARE_EXT 439 -+# define SSL_F_QUIC_CHANGE_CIPHER_STATE 639 -+# define SSL_F_QUIC_GET_MESSAGE 641 -+# define SSL_F_QUIC_SET_ENCRYPTION_SECRETS 642 - # define SSL_F_READ_STATE_MACHINE 352 - # define SSL_F_SET_CLIENT_CIPHERSUITE 540 - # define SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET 595 -@@ -106,7 +107,9 @@ int ERR_load_SSL_strings(void); - # define SSL_F_SSL3_CTRL 213 - # define SSL_F_SSL3_CTX_CTRL 133 - # define SSL_F_SSL3_DIGEST_CACHED_RECORDS 293 -+# define SSL_F_SSL3_DISPATCH_ALERT 643 - # define SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC 292 -+# define SSL_F_SSL3_DO_WRITE 644 - # define SSL_F_SSL3_ENC 608 - # define SSL_F_SSL3_FINAL_FINISH_MAC 285 - # define SSL_F_SSL3_FINISH_MAC 587 -@@ -211,6 +214,8 @@ int ERR_load_SSL_strings(void); - # define SSL_F_SSL_PEEK 270 - # define SSL_F_SSL_PEEK_EX 432 - # define SSL_F_SSL_PEEK_INTERNAL 522 -+# define SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE 645 -+# define SSL_F_SSL_PROVIDE_QUIC_DATA 646 - # define SSL_F_SSL_READ 223 - # define SSL_F_SSL_READ_EARLY_DATA 529 - # define SSL_F_SSL_READ_EX 434 -@@ -260,6 +265,7 @@ int ERR_load_SSL_strings(void); - # define SSL_F_SSL_WRITE_EARLY_FINISH 527 - # define SSL_F_SSL_WRITE_EX 433 - # define SSL_F_SSL_WRITE_INTERNAL 524 -+# define SSL_F_STATEM_FLUSH 647 - # define SSL_F_STATE_MACHINE 353 - # define SSL_F_TLS12_CHECK_PEER_SIGALG 333 - # define SSL_F_TLS12_COPY_SIGALGS 533 -@@ -319,6 +325,7 @@ int ERR_load_SSL_strings(void); - # define SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH 619 - # define SSL_F_TLS_CONSTRUCT_CTOS_PSK 501 - # define SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES 509 -+# define SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS 648 - # define SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE 473 - # define SSL_F_TLS_CONSTRUCT_CTOS_SCT 474 - # define SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME 475 -@@ -358,6 +365,7 @@ int ERR_load_SSL_strings(void); - # define SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN 548 - # define SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG 457 - # define SSL_F_TLS_CONSTRUCT_STOC_PSK 504 -+# define SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS 649 - # define SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE 458 - # define SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME 459 - # define SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET 460 -@@ -383,6 +391,7 @@ int ERR_load_SSL_strings(void); - # define SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH 620 - # define SSL_F_TLS_PARSE_CTOS_PSK 505 - # define SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES 572 -+# define SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS 650 - # define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE 464 - # define SSL_F_TLS_PARSE_CTOS_SERVER_NAME 573 - # define SSL_F_TLS_PARSE_CTOS_SESSION_TICKET 574 -@@ -401,6 +410,7 @@ int ERR_load_SSL_strings(void); - # define SSL_F_TLS_PARSE_STOC_MAXFRAGMENTLEN 581 - # define SSL_F_TLS_PARSE_STOC_NPN 582 - # define SSL_F_TLS_PARSE_STOC_PSK 502 -+# define SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS 651 - # define SSL_F_TLS_PARSE_STOC_RENEGOTIATE 448 - # define SSL_F_TLS_PARSE_STOC_SCT 564 - # define SSL_F_TLS_PARSE_STOC_SERVER_NAME 583 -@@ -565,6 +575,7 @@ int ERR_load_SSL_strings(void); - # define SSL_R_INCONSISTENT_EARLY_DATA_SNI 231 - # define SSL_R_INCONSISTENT_EXTMS 104 - # define SSL_R_INSUFFICIENT_SECURITY 241 -+# define SSL_R_INTERNAL_ERROR 295 - # define SSL_R_INVALID_ALERT 205 - # define SSL_R_INVALID_CCS_MESSAGE 260 - # define SSL_R_INVALID_CERTIFICATE_OR_ALG 238 -@@ -762,6 +773,7 @@ int ERR_load_SSL_strings(void); - # define SSL_R_WRONG_CERTIFICATE_TYPE 383 - # define SSL_R_WRONG_CIPHER_RETURNED 261 - # define SSL_R_WRONG_CURVE 378 -+# define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED 296 - # define SSL_R_WRONG_SIGNATURE_LENGTH 264 - # define SSL_R_WRONG_SIGNATURE_SIZE 265 - # define SSL_R_WRONG_SIGNATURE_TYPE 370 -diff --git a/deps/openssl/openssl/include/openssl/tls1.h b/deps/openssl/openssl/include/openssl/tls1.h -index 76d9fda46e..6e16c97316 100644 ---- a/deps/openssl/openssl/include/openssl/tls1.h -+++ b/deps/openssl/openssl/include/openssl/tls1.h -@@ -148,6 +148,9 @@ extern "C" { - /* Temporary extension type */ - # define TLSEXT_TYPE_renegotiate 0xff01 - -+/* ExtensionType value from draft-ietf-quic-tls-13 */ -+# define TLSEXT_TYPE_quic_transport_parameters 0xffa5 -+ - # ifndef OPENSSL_NO_NEXTPROTONEG - /* This is not an IANA defined extension number */ - # define TLSEXT_TYPE_next_proto_neg 13172 -diff --git a/deps/openssl/openssl/ssl/build.info b/deps/openssl/openssl/ssl/build.info -index bb2f1deb53..eec0d14f2c 100644 ---- a/deps/openssl/openssl/ssl/build.info -+++ b/deps/openssl/openssl/ssl/build.info -@@ -12,4 +12,5 @@ SOURCE[../libssl]=\ - ssl_asn1.c ssl_txt.c ssl_init.c ssl_conf.c ssl_mcnf.c \ - bio_ssl.c ssl_err.c tls_srp.c t1_trce.c ssl_utst.c \ - record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \ -- statem/statem.c record/ssl3_record_tls13.c -+ statem/statem.c record/ssl3_record_tls13.c \ -+ ssl_quic.c statem/statem_quic.c -diff --git a/deps/openssl/openssl/ssl/s3_msg.c b/deps/openssl/openssl/ssl/s3_msg.c -index 339fb2774a..8d3cd442aa 100644 ---- a/deps/openssl/openssl/ssl/s3_msg.c -+++ b/deps/openssl/openssl/ssl/s3_msg.c -@@ -1,5 +1,5 @@ - /* -- * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. -+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the OpenSSL license (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy -@@ -75,6 +75,16 @@ int ssl3_dispatch_alert(SSL *s) - - s->s3->alert_dispatch = 0; - alertlen = 2; -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s)) { -+ if (!s->quic_method->send_alert(s, s->quic_write_level, -+ s->s3->send_alert[1])) { -+ SSLerr(SSL_F_SSL3_DISPATCH_ALERT, SSL_R_INTERNAL_ERROR); -+ return 0; -+ } -+ i = 1; -+ } else -+#endif - i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], &alertlen, 1, 0, - &written); - if (i <= 0) { -diff --git a/deps/openssl/openssl/ssl/ssl_ciph.c b/deps/openssl/openssl/ssl/ssl_ciph.c -index 735a483c64..a3fe97597b 100644 ---- a/deps/openssl/openssl/ssl/ssl_ciph.c -+++ b/deps/openssl/openssl/ssl/ssl_ciph.c -@@ -2162,3 +2162,35 @@ int ssl_cert_is_disabled(size_t idx) - return 1; - return 0; - } -+ -+int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c) -+{ -+ switch (c->algorithm2 & (0xFF << TLS1_PRF_DGST_SHIFT)) { -+ default: -+ break; -+ case TLS1_PRF_SHA1_MD5: /* TLS1_PRF */ -+ return NID_md5_sha1; -+ case TLS1_PRF_SHA256: -+ return NID_sha256; -+ case TLS1_PRF_SHA384: -+ return NID_sha384; -+ case TLS1_PRF_GOST94: -+ return NID_id_GostR3411_94_prf; -+ case TLS1_PRF_GOST12_256: -+ return NID_id_GostR3411_2012_256; -+ case TLS1_PRF_GOST12_512: -+ return NID_id_GostR3411_2012_512; -+ } -+ /* TLSv1.3 ciphers don't specify separate PRF */ -+ switch (c->algorithm2 & SSL_HANDSHAKE_MAC_MASK) { -+ default: -+ break; -+ case SSL_HANDSHAKE_MAC_MD5_SHA1: /* SSL_HANDSHAKE_MAC_DEFAULT */ -+ return NID_md5_sha1; -+ case SSL_HANDSHAKE_MAC_SHA256: -+ return NID_sha256; -+ case SSL_HANDSHAKE_MAC_SHA384: -+ return NID_sha384; -+ } -+ return NID_undef; -+} -diff --git a/deps/openssl/openssl/ssl/ssl_err.c b/deps/openssl/openssl/ssl/ssl_err.c -index 4b12ed1485..3cdbee2ffa 100644 ---- a/deps/openssl/openssl/ssl/ssl_err.c -+++ b/deps/openssl/openssl/ssl/ssl_err.c -@@ -112,6 +112,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - "ossl_statem_server_post_process_message"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_POST_WORK, 0), - "ossl_statem_server_post_work"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_PRE_WORK, 0), -+ "ossl_statem_server_pre_work"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_PROCESS_MESSAGE, 0), - "ossl_statem_server_process_message"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION, 0), -@@ -122,6 +124,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - {ERR_PACK(ERR_LIB_SSL, SSL_F_PITEM_NEW, 0), "pitem_new"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_PQUEUE_NEW, 0), "pqueue_new"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_PROCESS_KEY_SHARE_EXT, 0), ""}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_CHANGE_CIPHER_STATE, 0), -+ "quic_change_cipher_state"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_GET_MESSAGE, 0), "quic_get_message"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, 0), -+ "quic_set_encryption_secrets"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_READ_STATE_MACHINE, 0), "read_state_machine"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SET_CLIENT_CIPHERSUITE, 0), - "set_client_ciphersuite"}, -@@ -139,8 +146,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_CTX_CTRL, 0), "ssl3_ctx_ctrl"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DIGEST_CACHED_RECORDS, 0), - "ssl3_digest_cached_records"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DISPATCH_ALERT, 0), -+ "ssl3_dispatch_alert"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC, 0), - "ssl3_do_change_cipher_spec"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_WRITE, 0), "ssl3_do_write"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_ENC, 0), "ssl3_enc"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_FINAL_FINISH_MAC, 0), - "ssl3_final_finish_mac"}, -@@ -302,6 +312,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK, 0), "SSL_peek"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_EX, 0), "SSL_peek_ex"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_INTERNAL, 0), "ssl_peek_internal"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE, 0), -+ "SSL_process_quic_post_handshake"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROVIDE_QUIC_DATA, 0), -+ "SSL_provide_quic_data"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ, 0), "SSL_read"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ_EARLY_DATA, 0), - "SSL_read_early_data"}, -@@ -378,6 +392,7 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EARLY_FINISH, 0), ""}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EX, 0), "SSL_write_ex"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_INTERNAL, 0), "ssl_write_internal"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_STATEM_FLUSH, 0), "statem_flush"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_STATE_MACHINE, 0), "state_machine"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS12_CHECK_PEER_SIGALG, 0), - "tls12_check_peer_sigalg"}, -@@ -479,6 +494,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - "tls_construct_ctos_psk"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES, 0), - "tls_construct_ctos_psk_kex_modes"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, 0), -+ "tls_construct_ctos_quic_transport_params"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE, 0), - "tls_construct_ctos_renegotiate"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_SCT, 0), -@@ -550,6 +567,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - "tls_construct_stoc_next_proto_neg"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_PSK, 0), - "tls_construct_stoc_psk"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, 0), -+ "tls_construct_stoc_quic_transport_params"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE, 0), - "tls_construct_stoc_renegotiate"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME, 0), -@@ -596,6 +615,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK, 0), "tls_parse_ctos_psk"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES, 0), - "tls_parse_ctos_psk_kex_modes"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, 0), -+ "tls_parse_ctos_quic_transport_params"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_RENEGOTIATE, 0), - "tls_parse_ctos_renegotiate"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_SERVER_NAME, 0), -@@ -628,6 +649,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { - "tls_parse_stoc_maxfragmentlen"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_NPN, 0), "tls_parse_stoc_npn"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_PSK, 0), "tls_parse_stoc_psk"}, -+ {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, 0), -+ "tls_parse_stoc_quic_transport_params"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_RENEGOTIATE, 0), - "tls_parse_stoc_renegotiate"}, - {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_SCT, 0), "tls_parse_stoc_sct"}, -@@ -905,6 +928,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EXTMS), "inconsistent extms"}, - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INSUFFICIENT_SECURITY), - "insufficient security"}, -+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INTERNAL_ERROR), "internal error"}, - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_ALERT), "invalid alert"}, - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CCS_MESSAGE), - "invalid ccs message"}, -@@ -1248,6 +1272,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CIPHER_RETURNED), - "wrong cipher returned"}, - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CURVE), "wrong curve"}, -+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED), -+ "wrong encryption level received"}, - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_LENGTH), - "wrong signature length"}, - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_SIZE), -diff --git a/deps/openssl/openssl/ssl/ssl_lib.c b/deps/openssl/openssl/ssl/ssl_lib.c -index b9adc45a8e..9b318de506 100644 ---- a/deps/openssl/openssl/ssl/ssl_lib.c -+++ b/deps/openssl/openssl/ssl/ssl_lib.c -@@ -839,6 +839,10 @@ SSL *SSL_new(SSL_CTX *ctx) - - s->job = NULL; - -+#ifndef OPENSSL_NO_QUIC -+ s->quic_method = ctx->quic_method; -+#endif -+ - #ifndef OPENSSL_NO_CT - if (!SSL_set_ct_validation_callback(s, ctx->ct_validation_callback, - ctx->ct_validation_callback_arg)) -@@ -1204,6 +1208,18 @@ void SSL_free(SSL *s) - OPENSSL_free(s->pha_context); - EVP_MD_CTX_free(s->pha_dgst); - -+#ifndef OPENSSL_NO_QUIC -+ OPENSSL_free(s->ext.quic_transport_params); -+ OPENSSL_free(s->ext.peer_quic_transport_params); -+ while (s->quic_input_data_head != NULL) { -+ QUIC_DATA *qd; -+ -+ qd = s->quic_input_data_head; -+ s->quic_input_data_head = qd->next; -+ OPENSSL_free(qd); -+ } -+#endif -+ - sk_X509_NAME_pop_free(s->ca_names, X509_NAME_free); - sk_X509_NAME_pop_free(s->client_ca_names, X509_NAME_free); - -@@ -1723,6 +1739,12 @@ static int ssl_io_intern(void *vargs) - - int ssl_read_internal(SSL *s, void *buf, size_t num, size_t *readbytes) - { -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s)) { -+ SSLerr(SSL_F_SSL_READ_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); -+ return -1; -+ } -+#endif - if (s->handshake_func == NULL) { - SSLerr(SSL_F_SSL_READ_INTERNAL, SSL_R_UNINITIALIZED); - return -1; -@@ -1855,6 +1877,12 @@ int SSL_get_early_data_status(const SSL *s) - - static int ssl_peek_internal(SSL *s, void *buf, size_t num, size_t *readbytes) - { -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s)) { -+ SSLerr(SSL_F_SSL_PEEK_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); -+ return -1; -+ } -+#endif - if (s->handshake_func == NULL) { - SSLerr(SSL_F_SSL_PEEK_INTERNAL, SSL_R_UNINITIALIZED); - return -1; -@@ -1915,6 +1943,12 @@ int SSL_peek_ex(SSL *s, void *buf, size_t num, size_t *readbytes) - - int ssl_write_internal(SSL *s, const void *buf, size_t num, size_t *written) - { -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s)) { -+ SSLerr(SSL_F_SSL_WRITE_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); -+ return -1; -+ } -+#endif - if (s->handshake_func == NULL) { - SSLerr(SSL_F_SSL_WRITE_INTERNAL, SSL_R_UNINITIALIZED); - return -1; -@@ -3565,6 +3599,11 @@ int SSL_get_error(const SSL *s, int i) - } - - if (SSL_want_read(s)) { -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s)) { -+ return SSL_ERROR_WANT_READ; -+ } -+#endif - bio = SSL_get_rbio(s); - if (BIO_should_read(bio)) - return SSL_ERROR_WANT_READ; -@@ -3943,7 +3982,7 @@ EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) - - const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) - { -- if ((s->session != NULL) && (s->session->cipher != NULL)) -+ if (s->session != NULL) - return s->session->cipher; - return NULL; - } -diff --git a/deps/openssl/openssl/ssl/ssl_local.h b/deps/openssl/openssl/ssl/ssl_local.h -index 8ddbde7729..016b253858 100644 ---- a/deps/openssl/openssl/ssl/ssl_local.h -+++ b/deps/openssl/openssl/ssl/ssl_local.h -@@ -315,6 +315,13 @@ - /* Flag used on OpenSSL ciphersuite ids to indicate they are for SSLv3+ */ - # define SSL3_CK_CIPHERSUITE_FLAG 0x03000000 - -+/* Check if an SSL structure is using QUIC (which uses TLSv1.3) */ -+# ifndef OPENSSL_NO_QUIC -+# define SSL_IS_QUIC(s) (s->quic_method != NULL) -+# else -+# define SSL_IS_QUIC(s) 0 -+# endif -+ - /* Check if an SSL structure is using DTLS */ - # define SSL_IS_DTLS(s) (s->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_DTLS) - -@@ -715,6 +722,7 @@ typedef enum tlsext_index_en { - TLSEXT_IDX_cryptopro_bug, - TLSEXT_IDX_early_data, - TLSEXT_IDX_certificate_authorities, -+ TLSEXT_IDX_quic_transport_params, - TLSEXT_IDX_padding, - TLSEXT_IDX_psk, - /* Dummy index - must always be the last entry */ -@@ -1064,7 +1072,24 @@ struct ssl_ctx_st { - - /* Do we advertise Post-handshake auth support? */ - int pha_enabled; -+ -+#ifndef OPENSSL_NO_QUIC -+ const SSL_QUIC_METHOD *quic_method; -+#endif -+}; -+ -+typedef struct cert_pkey_st CERT_PKEY; -+ -+#ifndef OPENSSL_NO_QUIC -+struct quic_data_st { -+ struct quic_data_st *next; -+ OSSL_ENCRYPTION_LEVEL level; -+ size_t offset; -+ size_t length; - }; -+typedef struct quic_data_st QUIC_DATA; -+int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level); -+#endif - - struct ssl_st { - /* -@@ -1153,6 +1178,11 @@ struct ssl_st { - unsigned char handshake_traffic_hash[EVP_MAX_MD_SIZE]; - unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE]; - unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE]; -+# ifndef OPENSSL_NO_QUIC -+ unsigned char client_hand_traffic_secret[EVP_MAX_MD_SIZE]; -+ unsigned char server_hand_traffic_secret[EVP_MAX_MD_SIZE]; -+ unsigned char client_early_traffic_secret[EVP_MAX_MD_SIZE]; -+# endif - unsigned char exporter_master_secret[EVP_MAX_MD_SIZE]; - unsigned char early_exporter_master_secret[EVP_MAX_MD_SIZE]; - EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */ -@@ -1365,8 +1395,22 @@ struct ssl_st { - * selected. - */ - int tick_identity; -+ -+#ifndef OPENSSL_NO_QUIC -+ uint8_t *quic_transport_params; -+ size_t quic_transport_params_len; -+ uint8_t *peer_quic_transport_params; -+ size_t peer_quic_transport_params_len; -+#endif - } ext; - -+#ifndef OPENSSL_NO_QUIC -+ OSSL_ENCRYPTION_LEVEL quic_read_level; -+ OSSL_ENCRYPTION_LEVEL quic_write_level; -+ QUIC_DATA *quic_input_data_head; -+ QUIC_DATA *quic_input_data_tail; -+ const SSL_QUIC_METHOD *quic_method; -+#endif - /* - * Parsed form of the ClientHello, kept around across client_hello_cb - * calls. -diff --git a/deps/openssl/openssl/ssl/ssl_quic.c b/deps/openssl/openssl/ssl/ssl_quic.c -new file mode 100644 -index 0000000000..2d8accbdd1 ---- /dev/null -+++ b/deps/openssl/openssl/ssl/ssl_quic.c -@@ -0,0 +1,285 @@ -+/* -+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. -+ * -+ * Licensed under the Apache License 2.0 (the "License"). You may not use -+ * this file except in compliance with the License. You can obtain a copy -+ * in the file LICENSE in the source distribution or at -+ * https://www.openssl.org/source/license.html -+ */ -+ -+#include "ssl_local.h" -+#include "internal/cryptlib.h" -+#include "internal/refcount.h" -+ -+#ifdef OPENSSL_NO_QUIC -+NON_EMPTY_TRANSLATION_UNIT -+#else -+ -+int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, -+ size_t params_len) -+{ -+ uint8_t *tmp; -+ -+ if (params == NULL || params_len == 0) { -+ tmp = NULL; -+ params_len = 0; -+ } else { -+ tmp = OPENSSL_memdup(params, params_len); -+ if (tmp == NULL) -+ return 0; -+ } -+ -+ OPENSSL_free(ssl->ext.quic_transport_params); -+ ssl->ext.quic_transport_params = tmp; -+ ssl->ext.quic_transport_params_len = params_len; -+ return 1; -+} -+ -+void SSL_get_peer_quic_transport_params(const SSL *ssl, -+ const uint8_t **out_params, -+ size_t *out_params_len) -+{ -+ *out_params = ssl->ext.peer_quic_transport_params; -+ *out_params_len = ssl->ext.peer_quic_transport_params_len; -+} -+ -+size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level) -+{ -+ /* -+ * Limits flights to 16K by default when there are no large -+ * (certificate-carrying) messages. -+ */ -+ static const size_t DEFAULT_FLIGHT_LIMIT = 16384; -+ -+ switch (level) { -+ case ssl_encryption_initial: -+ return DEFAULT_FLIGHT_LIMIT; -+ case ssl_encryption_early_data: -+ /* QUIC does not send EndOfEarlyData. */ -+ return 0; -+ case ssl_encryption_handshake: -+ if (ssl->server) { -+ /* -+ * Servers may receive Certificate message if configured to request -+ * client certificates. -+ */ -+ if ((ssl->verify_mode & SSL_VERIFY_PEER) -+ && ssl->max_cert_list > DEFAULT_FLIGHT_LIMIT) -+ return ssl->max_cert_list; -+ } else { -+ /* -+ * Clients may receive both Certificate message and a CertificateRequest -+ * message. -+ */ -+ if (2*ssl->max_cert_list > DEFAULT_FLIGHT_LIMIT) -+ return 2 * ssl->max_cert_list; -+ } -+ return DEFAULT_FLIGHT_LIMIT; -+ case ssl_encryption_application: -+ return DEFAULT_FLIGHT_LIMIT; -+ } -+ -+ return 0; -+} -+ -+OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl) -+{ -+ return ssl->quic_read_level; -+} -+ -+OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl) -+{ -+ return ssl->quic_write_level; -+} -+ -+int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *data, size_t len) -+{ -+ size_t l; -+ -+ if (!SSL_IS_QUIC(ssl)) { -+ SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); -+ return 0; -+ } -+ -+ /* Level can be different than the current read, but not less */ -+ if (level < ssl->quic_read_level -+ || (ssl->quic_input_data_tail != NULL && level < ssl->quic_input_data_tail->level)) { -+ SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); -+ return 0; -+ } -+ -+ /* Split the QUIC messages up, if necessary */ -+ while (len > 0) { -+ QUIC_DATA *qd; -+ const uint8_t *p = data + 1; -+ -+ /* Check for an incomplete block */ -+ qd = ssl->quic_input_data_tail; -+ if (qd != NULL) { -+ l = qd->length - qd->offset; -+ if (l != 0) { -+ /* we still need to copy `l` bytes into the last data block */ -+ if (l > len) -+ l = len; -+ memcpy((char*)(qd+1) + qd->offset, data, l); -+ qd->offset += l; -+ len -= l; -+ data += l; -+ continue; -+ } -+ } -+ -+ n2l3(p, l); -+ l += SSL3_HM_HEADER_LENGTH; -+ -+ qd = OPENSSL_zalloc(sizeof(QUIC_DATA) + l); -+ if (qd == NULL) { -+ SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_INTERNAL_ERROR); -+ return 0; -+ } -+ -+ qd->next = NULL; -+ qd->length = l; -+ qd->level = level; -+ /* partial data received? */ -+ if (l > len) -+ l = len; -+ qd->offset = l; -+ -+ memcpy((void*)(qd + 1), data, l); -+ if (ssl->quic_input_data_tail != NULL) -+ ssl->quic_input_data_tail->next = qd; -+ else -+ ssl->quic_input_data_head = qd; -+ ssl->quic_input_data_tail = qd; -+ -+ data += l; -+ len -= l; -+ } -+ -+ return 1; -+} -+ -+int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) -+{ -+ switch (ctx->method->version) { -+ case DTLS1_VERSION: -+ case DTLS1_2_VERSION: -+ case DTLS_ANY_VERSION: -+ case DTLS1_BAD_VER: -+ return 0; -+ default: -+ break; -+ } -+ ctx->quic_method = quic_method; -+ ctx->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; -+ return 1; -+} -+ -+int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method) -+{ -+ switch (ssl->method->version) { -+ case DTLS1_VERSION: -+ case DTLS1_2_VERSION: -+ case DTLS_ANY_VERSION: -+ case DTLS1_BAD_VER: -+ return 0; -+ default: -+ break; -+ } -+ ssl->quic_method = quic_method; -+ ssl->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; -+ return 1; -+} -+ -+int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) -+{ -+ uint8_t *c2s_secret = NULL; -+ uint8_t *s2c_secret = NULL; -+ size_t len; -+ const EVP_MD *md; -+ -+ if (!SSL_IS_QUIC(ssl)) -+ return 1; -+ -+ /* secrets from the POV of the client */ -+ switch (level) { -+ case ssl_encryption_early_data: -+ c2s_secret = ssl->client_early_traffic_secret; -+ break; -+ case ssl_encryption_handshake: -+ c2s_secret = ssl->client_hand_traffic_secret; -+ s2c_secret = ssl->server_hand_traffic_secret; -+ break; -+ case ssl_encryption_application: -+ c2s_secret = ssl->client_app_traffic_secret; -+ s2c_secret = ssl->server_app_traffic_secret; -+ break; -+ default: -+ return 1; -+ } -+ -+ md = ssl_handshake_md(ssl); -+ if (md == NULL) { -+ /* May not have selected cipher, yet */ -+ const SSL_CIPHER *c = NULL; -+ -+ if (ssl->session != NULL) -+ c = SSL_SESSION_get0_cipher(ssl->session); -+ else if (ssl->psksession != NULL) -+ c = SSL_SESSION_get0_cipher(ssl->psksession); -+ -+ if (c != NULL) -+ md = SSL_CIPHER_get_handshake_digest(c); -+ } -+ -+ if ((len = EVP_MD_size(md)) <= 0) { -+ SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, -+ ERR_R_INTERNAL_ERROR); -+ return 0; -+ } -+ -+ if (ssl->server) { -+ if (!ssl->quic_method->set_encryption_secrets(ssl, level, c2s_secret, -+ s2c_secret, len)) { -+ SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, -+ ERR_R_INTERNAL_ERROR); -+ return 0; -+ } -+ } else { -+ if (!ssl->quic_method->set_encryption_secrets(ssl, level, s2c_secret, -+ c2s_secret, len)) { -+ SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, -+ ERR_R_INTERNAL_ERROR); -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int SSL_process_quic_post_handshake(SSL *ssl) -+{ -+ int ret; -+ -+ if (SSL_in_init(ssl) || !SSL_IS_QUIC(ssl)) { -+ SSLerr(SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); -+ return 0; -+ } -+ -+ ossl_statem_set_in_init(ssl, 1); -+ ret = ssl->handshake_func(ssl); -+ ossl_statem_set_in_init(ssl, 0); -+ -+ if (ret <= 0) -+ return 0; -+ return 1; -+} -+ -+int SSL_is_quic(SSL* ssl) -+{ -+ return SSL_IS_QUIC(ssl); -+} -+ -+#endif -diff --git a/deps/openssl/openssl/ssl/statem/extensions.c b/deps/openssl/openssl/ssl/statem/extensions.c -index 4ef8b417b8..be09afbe71 100644 ---- a/deps/openssl/openssl/ssl/statem/extensions.c -+++ b/deps/openssl/openssl/ssl/statem/extensions.c -@@ -56,6 +56,10 @@ static int final_sig_algs(SSL *s, unsigned int context, int sent); - static int final_early_data(SSL *s, unsigned int context, int sent); - static int final_maxfragmentlen(SSL *s, unsigned int context, int sent); - static int init_post_handshake_auth(SSL *s, unsigned int context); -+#ifndef OPENSSL_NO_QUIC -+static int init_quic_transport_params(SSL *s, unsigned int context); -+static int final_quic_transport_params(SSL *s, unsigned int context, int sent); -+#endif - - /* Structure to define a built-in extension */ - typedef struct extensions_definition_st { -@@ -373,6 +377,19 @@ static const EXTENSION_DEFINITION ext_defs[] = { - tls_construct_certificate_authorities, - tls_construct_certificate_authorities, NULL, - }, -+#ifndef OPENSSL_NO_QUIC -+ { -+ TLSEXT_TYPE_quic_transport_parameters, -+ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS -+ | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY, -+ init_quic_transport_params, -+ tls_parse_ctos_quic_transport_params, tls_parse_stoc_quic_transport_params, -+ tls_construct_stoc_quic_transport_params, tls_construct_ctos_quic_transport_params, -+ final_quic_transport_params, -+ }, -+#else -+ INVALID_EXTENSION, -+#endif - { - /* Must be immediately before pre_shared_key */ - TLSEXT_TYPE_padding, -@@ -1701,3 +1718,15 @@ static int init_post_handshake_auth(SSL *s, unsigned int context) - - return 1; - } -+ -+#ifndef OPENSSL_NO_QUIC -+static int init_quic_transport_params(SSL *s, unsigned int context) -+{ -+ return 1; -+} -+ -+static int final_quic_transport_params(SSL *s, unsigned int context, int sent) -+{ -+ return 1; -+} -+#endif -diff --git a/deps/openssl/openssl/ssl/statem/extensions_clnt.c b/deps/openssl/openssl/ssl/statem/extensions_clnt.c -index bcce0f1d95..a9f73f07dc 100644 ---- a/deps/openssl/openssl/ssl/statem/extensions_clnt.c -+++ b/deps/openssl/openssl/ssl/statem/extensions_clnt.c -@@ -1214,7 +1214,28 @@ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, - #endif - } - -+#ifndef OPENSSL_NO_QUIC -+/* SAME AS tls_construct_stoc_quic_transport_params() */ -+EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, -+ unsigned int context, X509 *x, -+ size_t chainidx) -+{ -+ if (s->ext.quic_transport_params == NULL -+ || s->ext.quic_transport_params_len == 0) { -+ return EXT_RETURN_NOT_SENT; -+ } -+ -+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters) -+ || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, -+ s->ext.quic_transport_params_len)) { -+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, -+ SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); -+ return EXT_RETURN_FAIL; -+ } - -+ return EXT_RETURN_SENT; -+} -+#endif - /* - * Parse the server's renegotiation binding and abort if it's not right - */ -@@ -1912,6 +1933,18 @@ int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context, - return 0; - } - -+#ifndef OPENSSL_NO_QUIC -+ /* -+ * QUIC server must send 0xFFFFFFFF or it's a PROTOCOL_VIOLATION -+ * per draft-ietf-quic-tls-24 S4.5 -+ */ -+ if (s->quic_method != NULL && max_early_data != 0xFFFFFFFF) { -+ SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_EARLY_DATA, -+ SSL_R_INVALID_MAX_EARLY_DATA); -+ return 0; -+ } -+#endif -+ - s->session->ext.max_early_data = max_early_data; - - return 1; -@@ -1999,3 +2032,22 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, - - return 1; - } -+#ifndef OPENSSL_NO_QUIC -+/* SAME AS tls_parse_ctos_quic_transport_params() */ -+int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, -+ X509 *x, size_t chainidx) -+{ -+ OPENSSL_free(s->ext.peer_quic_transport_params); -+ s->ext.peer_quic_transport_params = NULL; -+ s->ext.peer_quic_transport_params_len = 0; -+ -+ if (!PACKET_memdup(pkt, -+ &s->ext.peer_quic_transport_params, -+ &s->ext.peer_quic_transport_params_len)) { -+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, -+ SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); -+ return 0; -+ } -+ return 1; -+} -+#endif -diff --git a/deps/openssl/openssl/ssl/statem/extensions_srvr.c b/deps/openssl/openssl/ssl/statem/extensions_srvr.c -index 3b07c6b940..602c9da314 100644 ---- a/deps/openssl/openssl/ssl/statem/extensions_srvr.c -+++ b/deps/openssl/openssl/ssl/statem/extensions_srvr.c -@@ -1303,6 +1303,26 @@ int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context - return 1; - } - -+#ifndef OPENSSL_NO_QUIC -+/* SAME AS tls_parse_stoc_quic_transport_params() */ -+int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, -+ X509 *x, size_t chainidx) -+{ -+ OPENSSL_free(s->ext.peer_quic_transport_params); -+ s->ext.peer_quic_transport_params = NULL; -+ s->ext.peer_quic_transport_params_len = 0; -+ -+ if (!PACKET_memdup(pkt, -+ &s->ext.peer_quic_transport_params, -+ &s->ext.peer_quic_transport_params_len)) { -+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, -+ SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); -+ return 0; -+ } -+ return 1; -+} -+#endif -+ - /* - * Add the server's renegotiation binding - */ -@@ -1926,12 +1946,20 @@ EXT_RETURN tls_construct_stoc_early_data(SSL *s, WPACKET *pkt, - size_t chainidx) - { - if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) { -- if (s->max_early_data == 0) -+ uint32_t max_early_data = s->max_early_data; -+ -+ if (max_early_data == 0) - return EXT_RETURN_NOT_SENT; - -+#ifndef OPENSSL_NO_QUIC -+ /* QUIC server must always send 0xFFFFFFFF, per draft-ietf-quic-tls-24 S4.5 */ -+ if (s->quic_method != NULL) -+ max_early_data = 0xFFFFFFFF; -+#endif -+ - if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data) - || !WPACKET_start_sub_packet_u16(pkt) -- || !WPACKET_put_bytes_u32(pkt, s->max_early_data) -+ || !WPACKET_put_bytes_u32(pkt, max_early_data) - || !WPACKET_close(pkt)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA, ERR_R_INTERNAL_ERROR); -@@ -1972,3 +2000,26 @@ EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, - - return EXT_RETURN_SENT; - } -+ -+#ifndef OPENSSL_NO_QUIC -+/* SAME AS tls_construct_ctos_quic_transport_params() */ -+EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, -+ unsigned int context, X509 *x, -+ size_t chainidx) -+{ -+ if (s->ext.quic_transport_params == NULL -+ || s->ext.quic_transport_params_len == 0) { -+ return EXT_RETURN_NOT_SENT; -+ } -+ -+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters) -+ || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, -+ s->ext.quic_transport_params_len)) { -+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, -+ SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); -+ return EXT_RETURN_FAIL; -+ } -+ -+ return EXT_RETURN_SENT; -+} -+#endif -diff --git a/deps/openssl/openssl/ssl/statem/statem.c b/deps/openssl/openssl/ssl/statem/statem.c -index 20f5bd584e..0a8acedebf 100644 ---- a/deps/openssl/openssl/ssl/statem/statem.c -+++ b/deps/openssl/openssl/ssl/statem/statem.c -@@ -575,6 +575,10 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) - * In DTLS we get the whole message in one go - header and body - */ - ret = dtls_get_message(s, &mt, &len); -+#ifndef OPENSSL_NO_QUIC -+ } else if (SSL_IS_QUIC(s)) { -+ ret = quic_get_message(s, &mt, &len); -+#endif - } else { - ret = tls_get_message_header(s, &mt); - } -@@ -604,8 +608,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) - return SUB_STATE_ERROR; - } - -- /* dtls_get_message already did this */ -- if (!SSL_IS_DTLS(s) -+ /* dtls_get_message/quic_get_message already did this */ -+ if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s) - && s->s3->tmp.message_size > 0 - && !grow_init_buf(s, s->s3->tmp.message_size - + SSL3_HM_HEADER_LENGTH)) { -@@ -618,8 +622,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) - /* Fall through */ - - case READ_STATE_BODY: -- if (!SSL_IS_DTLS(s)) { -- /* We already got this above for DTLS */ -+ if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s)) { -+ /* We already got this above for DTLS & QUIC */ - ret = tls_get_message_body(s, &len); - if (ret == 0) { - /* Could be non-blocking IO */ -@@ -900,6 +904,15 @@ static SUB_STATE_RETURN write_state_machine(SSL *s) - int statem_flush(SSL *s) - { - s->rwstate = SSL_WRITING; -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s)) { -+ if (!s->quic_method->flush_flight(s)) { -+ /* NOTE: BIO_flush() does not generate an error */ -+ SSLerr(SSL_F_STATEM_FLUSH, ERR_R_INTERNAL_ERROR); -+ return 0; -+ } -+ } else -+#endif - if (BIO_flush(s->wbio) <= 0) { - return 0; - } -diff --git a/deps/openssl/openssl/ssl/statem/statem_clnt.c b/deps/openssl/openssl/ssl/statem/statem_clnt.c -index 64e392cfbf..aa2f5ad3f4 100644 ---- a/deps/openssl/openssl/ssl/statem/statem_clnt.c -+++ b/deps/openssl/openssl/ssl/statem/statem_clnt.c -@@ -909,6 +909,14 @@ int ossl_statem_client_construct_message(SSL *s, WPACKET *pkt, - break; - - case TLS_ST_CW_END_OF_EARLY_DATA: -+#ifndef OPENSSL_NO_QUIC -+ /* QUIC does not send EndOfEarlyData, draft-ietf-quic-tls-24 S8.3 */ -+ if (s->quic_method != NULL) { -+ *confunc = NULL; -+ *mt = SSL3_MT_DUMMY; -+ break; -+ } -+#endif - *confunc = tls_construct_end_of_early_data; - *mt = SSL3_MT_END_OF_EARLY_DATA; - break; -diff --git a/deps/openssl/openssl/ssl/statem/statem_lib.c b/deps/openssl/openssl/ssl/statem/statem_lib.c -index 43d6fd5de9..09e7575832 100644 ---- a/deps/openssl/openssl/ssl/statem/statem_lib.c -+++ b/deps/openssl/openssl/ssl/statem/statem_lib.c -@@ -42,9 +42,23 @@ int ssl3_do_write(SSL *s, int type) - { - int ret; - size_t written = 0; -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s) && type == SSL3_RT_HANDSHAKE) { -+ ret = s->quic_method->add_handshake_data(s, s->quic_write_level, -+ (const uint8_t*)&s->init_buf->data[s->init_off], -+ s->init_num); -+ if (!ret) { -+ ret = -1; -+ /* QUIC can't sent anything out sice the above failed */ -+ SSLerr(SSL_F_SSL3_DO_WRITE, SSL_R_INTERNAL_ERROR); -+ } else { -+ written = s->init_num; -+ } -+ } else -+#endif -+ ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], -+ s->init_num, &written); - -- ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], -- s->init_num, &written); - if (ret < 0) - return -1; - if (type == SSL3_RT_HANDSHAKE) -@@ -1157,6 +1171,7 @@ int tls_get_message_header(SSL *s, int *mt) - - do { - while (s->init_num < SSL3_HM_HEADER_LENGTH) { -+ /* QUIC: either create a special ssl_read_bytes... or if/else this */ - i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &recvd_type, - &p[s->init_num], - SSL3_HM_HEADER_LENGTH - s->init_num, -diff --git a/deps/openssl/openssl/ssl/statem/statem_local.h b/deps/openssl/openssl/ssl/statem/statem_local.h -index e27c0c13a2..1551dac952 100644 ---- a/deps/openssl/openssl/ssl/statem/statem_local.h -+++ b/deps/openssl/openssl/ssl/statem/statem_local.h -@@ -93,6 +93,7 @@ WORK_STATE ossl_statem_server_post_process_message(SSL *s, WORK_STATE wst); - __owur int tls_get_message_header(SSL *s, int *mt); - __owur int tls_get_message_body(SSL *s, size_t *len); - __owur int dtls_get_message(SSL *s, int *mt, size_t *len); -+__owur int quic_get_message(SSL *s, int *mt, size_t *len); - - /* Message construction and processing functions */ - __owur int tls_process_initial_server_flight(SSL *s); -@@ -236,6 +237,10 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, - size_t chainidx); - int tls_parse_ctos_post_handshake_auth(SSL *, PACKET *pkt, unsigned int context, - X509 *x, size_t chainidx); -+#ifndef OPENSSL_NO_QUIC -+int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, -+ X509 *x, size_t chainidx); -+#endif - - EXT_RETURN tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, - unsigned int context, X509 *x, -@@ -298,6 +303,11 @@ EXT_RETURN tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, - size_t chainidx); - EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, - X509 *x, size_t chainidx); -+#ifndef OPENSSL_NO_QUIC -+EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, -+ unsigned int context, X509 *x, -+ size_t chainidx); -+#endif - - /* Client Extension processing */ - EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, -@@ -368,6 +378,11 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context, - X509 *x, size_t chainidx); - EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, unsigned int context, - X509 *x, size_t chainidx); -+#ifndef OPENSSL_NO_QUIC -+EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, -+ unsigned int context, X509 *x, -+ size_t chainidx); -+#endif - - int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context, - X509 *x, size_t chainidx); -@@ -413,6 +428,10 @@ int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x, - size_t chainidx); - int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, - size_t chainidx); -+#ifndef OPENSSL_NO_QUIC -+int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, -+ X509 *x, size_t chainidx); -+#endif - - int tls_handle_alpn(SSL *s); - -diff --git a/deps/openssl/openssl/ssl/statem/statem_quic.c b/deps/openssl/openssl/ssl/statem/statem_quic.c -new file mode 100644 -index 0000000000..eb1a76ec9d ---- /dev/null -+++ b/deps/openssl/openssl/ssl/statem/statem_quic.c -@@ -0,0 +1,109 @@ -+/* -+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. -+ * -+ * Licensed under the Apache License 2.0 (the "License"). You may not use -+ * this file except in compliance with the License. You can obtain a copy -+ * in the file LICENSE in the source distribution or at -+ * https://www.openssl.org/source/license.html -+ */ -+ -+#include "../ssl_local.h" -+#include "statem_local.h" -+#include "internal/cryptlib.h" -+ -+#ifdef OPENSSL_NO_QUIC -+NON_EMPTY_TRANSLATION_UNIT -+#else -+ -+int quic_get_message(SSL *s, int *mt, size_t *len) -+{ -+ size_t l; -+ QUIC_DATA *qd = s->quic_input_data_head; -+ uint8_t *p; -+ -+ if (qd == NULL || (qd->length - qd->offset) != 0) { -+ s->rwstate = SSL_READING; -+ *len = 0; -+ return 0; -+ } -+ -+ /* This is where we check for the proper level, not when data is given */ -+ if (qd->level != s->quic_read_level) { -+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, -+ SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); -+ *len = 0; -+ return 0; -+ } -+ -+ if (!BUF_MEM_grow_clean(s->init_buf, (int)qd->length)) { -+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, -+ ERR_R_BUF_LIB); -+ *len = 0; -+ return 0; -+ } -+ -+ /* Copy buffered data */ -+ memcpy(s->init_buf->data, (void*)(qd + 1), qd->length); -+ s->init_buf->length = qd->length; -+ s->quic_input_data_head = qd->next; -+ if (s->quic_input_data_head == NULL) -+ s->quic_input_data_tail = NULL; -+ OPENSSL_free(qd); -+ -+ s->s3->tmp.message_type = *mt = *(s->init_buf->data); -+ p = (uint8_t*)s->init_buf->data + 1; -+ n2l3(p, l); -+ s->init_num = s->s3->tmp.message_size = *len = l; -+ s->init_msg = s->init_buf->data + SSL3_HM_HEADER_LENGTH; -+ -+ /* No CCS in QUIC/TLSv1.3? */ -+ if (*mt == SSL3_MT_CHANGE_CIPHER_SPEC) { -+ SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, -+ SSL_F_QUIC_GET_MESSAGE, -+ SSL_R_CCS_RECEIVED_EARLY); -+ *len = 0; -+ return 0; -+ } -+ -+ /* -+ * If receiving Finished, record MAC of prior handshake messages for -+ * Finished verification. -+ */ -+ if (*mt == SSL3_MT_FINISHED && !ssl3_take_mac(s)) { -+ /* SSLfatal() already called */ -+ *len = 0; -+ return 0; -+ } -+ -+ /* -+ * We defer feeding in the HRR until later. We'll do it as part of -+ * processing the message -+ * The TLsv1.3 handshake transcript stops at the ClientFinished -+ * message. -+ */ -+#define SERVER_HELLO_RANDOM_OFFSET (SSL3_HM_HEADER_LENGTH + 2) -+ /* KeyUpdate and NewSessionTicket do not need to be added */ -+ if (!SSL_IS_TLS13(s) || (s->s3->tmp.message_type != SSL3_MT_NEWSESSION_TICKET -+ && s->s3->tmp.message_type != SSL3_MT_KEY_UPDATE)) { -+ if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO -+ || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE -+ || memcmp(hrrrandom, -+ s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET, -+ SSL3_RANDOM_SIZE) != 0) { -+ if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, -+ s->init_num + SSL3_HM_HEADER_LENGTH)) { -+ /* SSLfatal() already called */ -+ *len = 0; -+ return 0; -+ } -+ } -+ } -+ if (s->msg_callback) -+ s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data, -+ (size_t)s->init_num + SSL3_HM_HEADER_LENGTH, s, -+ s->msg_callback_arg); -+ -+ return 1; -+} -+ -+#endif -diff --git a/deps/openssl/openssl/ssl/statem/statem_srvr.c b/deps/openssl/openssl/ssl/statem/statem_srvr.c -index 14cb27e6db..ffa89d3b54 100644 ---- a/deps/openssl/openssl/ssl/statem/statem_srvr.c -+++ b/deps/openssl/openssl/ssl/statem/statem_srvr.c -@@ -72,7 +72,8 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt) - return 1; - } - break; -- } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) { -+ } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED -+ && !SSL_IS_QUIC(s)) { - if (mt == SSL3_MT_END_OF_EARLY_DATA) { - st->hand_state = TLS_ST_SR_END_OF_EARLY_DATA; - return 1; -diff --git a/deps/openssl/openssl/ssl/tls13_enc.c b/deps/openssl/openssl/ssl/tls13_enc.c -index 86754dc967..a05401bfdc 100644 ---- a/deps/openssl/openssl/ssl/tls13_enc.c -+++ b/deps/openssl/openssl/ssl/tls13_enc.c -@@ -427,27 +427,140 @@ static int derive_secret_key_and_iv(SSL *s, int sending, const EVP_MD *md, - return 0; - } - --int tls13_change_cipher_state(SSL *s, int which) --{ - #ifdef CHARSET_EBCDIC -- static const unsigned char client_early_traffic[] = {0x63, 0x20, 0x65, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -- static const unsigned char client_handshake_traffic[] = {0x63, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -- static const unsigned char client_application_traffic[] = {0x63, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -- static const unsigned char server_handshake_traffic[] = {0x73, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -- static const unsigned char server_application_traffic[] = {0x73, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -- static const unsigned char exporter_master_secret[] = {0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; -- static const unsigned char resumption_master_secret[] = {0x72, 0x65, 0x73, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; -- static const unsigned char early_exporter_master_secret[] = {0x65, 0x20, 0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; -+static const unsigned char client_early_traffic[] = {0x63, 0x20, 0x65, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -+static const unsigned char client_handshake_traffic[] = {0x63, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -+static const unsigned char client_application_traffic[] = {0x63, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -+static const unsigned char server_handshake_traffic[] = {0x73, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -+static const unsigned char server_application_traffic[] = {0x73, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; -+static const unsigned char exporter_master_secret[] = {0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; -+static const unsigned char resumption_master_secret[] = {0x72, 0x65, 0x73, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; -+static const unsigned char early_exporter_master_secret[] = {0x65, 0x20, 0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; - #else -- static const unsigned char client_early_traffic[] = "c e traffic"; -- static const unsigned char client_handshake_traffic[] = "c hs traffic"; -- static const unsigned char client_application_traffic[] = "c ap traffic"; -- static const unsigned char server_handshake_traffic[] = "s hs traffic"; -- static const unsigned char server_application_traffic[] = "s ap traffic"; -- static const unsigned char exporter_master_secret[] = "exp master"; -- static const unsigned char resumption_master_secret[] = "res master"; -- static const unsigned char early_exporter_master_secret[] = "e exp master"; -+static const unsigned char client_early_traffic[] = "c e traffic"; -+static const unsigned char client_handshake_traffic[] = "c hs traffic"; -+static const unsigned char client_application_traffic[] = "c ap traffic"; -+static const unsigned char server_handshake_traffic[] = "s hs traffic"; -+static const unsigned char server_application_traffic[] = "s ap traffic"; -+static const unsigned char exporter_master_secret[] = "exp master"; -+static const unsigned char resumption_master_secret[] = "res master"; -+static const unsigned char early_exporter_master_secret[] = "e exp master"; - #endif -+#ifndef OPENSSL_NO_QUIC -+static int quic_change_cipher_state(SSL *s, int which) -+{ -+ unsigned char hash[EVP_MAX_MD_SIZE]; -+ size_t hashlen = 0; -+ int hashleni; -+ int ret = 0; -+ const EVP_MD *md = NULL; -+ OSSL_ENCRYPTION_LEVEL level = ssl_encryption_initial; -+ int is_handshake = ((which & SSL3_CC_HANDSHAKE) == SSL3_CC_HANDSHAKE); -+ int is_client_read = ((which & SSL3_CHANGE_CIPHER_CLIENT_READ) == SSL3_CHANGE_CIPHER_CLIENT_READ); -+ int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE); -+ int is_early = (which & SSL3_CC_EARLY); -+ -+ md = ssl_handshake_md(s); -+ if (!ssl3_digest_cached_records(s, 1) -+ || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) { -+ /* SSLfatal() already called */; -+ goto err; -+ } -+ -+ /* Ensure cast to size_t is safe */ -+ hashleni = EVP_MD_size(md); -+ if (!ossl_assert(hashleni >= 0)) { -+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, -+ ERR_R_EVP_LIB); -+ goto err; -+ } -+ hashlen = (size_t)hashleni; -+ -+ if (is_client_read || is_server_write) { -+ if (is_handshake) { -+ level = ssl_encryption_handshake; -+ -+ if (!tls13_hkdf_expand(s, md, s->handshake_secret, client_handshake_traffic, -+ sizeof(client_handshake_traffic)-1, hash, hashlen, -+ s->client_hand_traffic_secret, hashlen, 1) -+ || !ssl_log_secret(s, CLIENT_HANDSHAKE_LABEL, s->client_hand_traffic_secret, hashlen) -+ || !tls13_derive_finishedkey(s, md, s->client_hand_traffic_secret, -+ s->client_finished_secret, hashlen) -+ || !tls13_hkdf_expand(s, md, s->handshake_secret, server_handshake_traffic, -+ sizeof(server_handshake_traffic)-1, hash, hashlen, -+ s->server_hand_traffic_secret, hashlen, 1) -+ || !ssl_log_secret(s, SERVER_HANDSHAKE_LABEL, s->server_hand_traffic_secret, hashlen) -+ || !tls13_derive_finishedkey(s, md, s->server_hand_traffic_secret, -+ s->server_finished_secret, hashlen)) { -+ /* SSLfatal() already called */ -+ goto err; -+ } -+ } else { -+ level = ssl_encryption_application; -+ -+ if (!tls13_hkdf_expand(s, md, s->master_secret, client_application_traffic, -+ sizeof(client_application_traffic)-1, hash, hashlen, -+ s->client_app_traffic_secret, hashlen, 1) -+ || !ssl_log_secret(s, CLIENT_APPLICATION_LABEL, s->client_app_traffic_secret, hashlen) -+ || !tls13_hkdf_expand(s, md, s->master_secret, server_application_traffic, -+ sizeof(server_application_traffic)-1, hash, hashlen, -+ s->server_app_traffic_secret, hashlen, 1) -+ || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, s->server_app_traffic_secret, hashlen)) { -+ /* SSLfatal() already called */ -+ goto err; -+ } -+ } -+ if (!quic_set_encryption_secrets(s, level)) { -+ /* SSLfatal() already called */ -+ goto err; -+ } -+ if (s->server) -+ s->quic_write_level = level; -+ else -+ s->quic_read_level = level; -+ } else { -+ /* is_client_write || is_server_read */ -+ -+ if (is_early) { -+ level = ssl_encryption_early_data; -+ -+ if (!tls13_hkdf_expand(s, md, s->early_secret, client_early_traffic, -+ sizeof(client_early_traffic)-1, hash, hashlen, -+ s->client_early_traffic_secret, hashlen, 1) -+ || !ssl_log_secret(s, CLIENT_EARLY_LABEL, s->client_early_traffic_secret, hashlen) -+ || !quic_set_encryption_secrets(s, level)) { -+ /* SSLfatal() already called */ -+ goto err; -+ } -+ } else if (is_handshake) { -+ level = ssl_encryption_handshake; -+ } else { -+ level = ssl_encryption_application; -+ /* -+ * We also create the resumption master secret, but this time use the -+ * hash for the whole handshake including the Client Finished -+ */ -+ if (!tls13_hkdf_expand(s, md, s->master_secret, resumption_master_secret, -+ sizeof(resumption_master_secret)-1, hash, hashlen, -+ s->resumption_master_secret, hashlen, 1)) { -+ /* SSLfatal() already called */ -+ goto err; -+ } -+ } -+ -+ if (s->server) -+ s->quic_read_level = level; -+ else -+ s->quic_write_level = level; -+ } -+ -+ ret = 1; -+ err: -+ return ret; -+} -+#endif /* OPENSSL_NO_QUIC */ -+int tls13_change_cipher_state(SSL *s, int which) -+{ - unsigned char *iv; - unsigned char secret[EVP_MAX_MD_SIZE]; - unsigned char hashval[EVP_MAX_MD_SIZE]; -@@ -463,6 +576,11 @@ int tls13_change_cipher_state(SSL *s, int which) - const EVP_MD *md = NULL; - const EVP_CIPHER *cipher = NULL; - -+#ifndef OPENSSL_NO_QUIC -+ if (SSL_IS_QUIC(s)) -+ return quic_change_cipher_state(s, which); -+#endif -+ - if (which & SSL3_CC_READ) { - if (s->enc_read_ctx != NULL) { - EVP_CIPHER_CTX_reset(s->enc_read_ctx); -@@ -707,6 +825,7 @@ int tls13_change_cipher_state(SSL *s, int which) - s->statem.enc_write_state = ENC_WRITE_STATE_WRITE_PLAIN_ALERTS; - else - s->statem.enc_write_state = ENC_WRITE_STATE_VALID; -+ - ret = 1; - err: - OPENSSL_cleanse(secret, sizeof(secret)); -diff --git a/deps/openssl/openssl/test/sslapitest.c b/deps/openssl/openssl/test/sslapitest.c -index 5c118108ef..fc06e4faa0 100644 ---- a/deps/openssl/openssl/test/sslapitest.c -+++ b/deps/openssl/openssl/test/sslapitest.c -@@ -6471,6 +6471,135 @@ static int test_servername(int tst) - return testresult; - } - -+#ifndef OPENSSL_NO_QUIC -+ -+static int test_quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *read_secret, -+ const uint8_t *write_secret, size_t secret_len) -+{ -+ test_printf_stderr("quic_set_encryption_secrets() %s, lvl=%d, len=%zd\n", -+ ssl->server ? "server" : "client", level, secret_len); -+ return 1; -+} -+static int test_quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, -+ const uint8_t *data, size_t len) -+{ -+ SSL *peer = (SSL*)SSL_get_app_data(ssl); -+ -+ test_printf_stderr("quic_add_handshake_data() %s, lvl=%d, *data=0x%02X, len=%zd\n", -+ ssl->server ? "server" : "client", level, (int)*data, len); -+ if (!TEST_ptr(peer)) -+ return 0; -+ -+ if (!TEST_true(SSL_provide_quic_data(peer, level, data, len))) { -+ ERR_print_errors_fp(stderr); -+ return 0; -+ } -+ -+ return 1; -+} -+static int test_quic_flush_flight(SSL *ssl) -+{ -+ test_printf_stderr("quic_flush_flight() %s\n", ssl->server ? "server" : "client"); -+ return 1; -+} -+static int test_quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) -+{ -+ test_printf_stderr("quic_send_alert() %s, lvl=%d, alert=%d\n", -+ ssl->server ? "server" : "client", level, alert); -+ return 1; -+} -+ -+static SSL_QUIC_METHOD quic_method = { -+ test_quic_set_encryption_secrets, -+ test_quic_add_handshake_data, -+ test_quic_flush_flight, -+ test_quic_send_alert, -+}; -+static int test_quic_api(void) -+{ -+ SSL_CTX *cctx = NULL, *sctx = NULL; -+ SSL *clientssl = NULL, *serverssl = NULL; -+ int testresult = 0; -+ static const char *server_str = "SERVER"; -+ static const char *client_str = "CLIENT"; -+ const uint8_t *peer_str; -+ size_t peer_str_len; -+ -+ /* Clean up logging space */ -+ memset(client_log_buffer, 0, sizeof(client_log_buffer)); -+ memset(server_log_buffer, 0, sizeof(server_log_buffer)); -+ client_log_buffer_index = 0; -+ server_log_buffer_index = 0; -+ error_writing_log = 0; -+ -+ -+ if (!TEST_ptr(sctx = SSL_CTX_new(TLS_server_method())) -+ || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) -+ || !TEST_ptr(sctx->quic_method) -+ || !TEST_ptr(serverssl = SSL_new(sctx)) -+ || !TEST_true(SSL_IS_QUIC(serverssl)) -+ || !TEST_true(SSL_set_quic_method(serverssl, NULL)) -+ || !TEST_false(SSL_IS_QUIC(serverssl)) -+ || !TEST_true(SSL_set_quic_method(serverssl, &quic_method)) -+ || !TEST_true(SSL_IS_QUIC(serverssl))) -+ goto end; -+ -+ SSL_CTX_free(sctx); -+ sctx = NULL; -+ SSL_free(serverssl); -+ serverssl = NULL; -+ -+ if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), -+ TLS_client_method(), -+ TLS1_3_VERSION, 0, -+ &sctx, &cctx, cert, privkey)) -+ || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) -+ || !TEST_true(SSL_CTX_set_quic_method(cctx, &quic_method)) -+ || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, -+ &clientssl, NULL, NULL)) -+ || !TEST_true(SSL_set_quic_transport_params(serverssl, -+ (unsigned char*)server_str, -+ sizeof(server_str))) -+ || !TEST_true(SSL_set_quic_transport_params(clientssl, -+ (unsigned char*)client_str, -+ sizeof(client_str))) -+ || !TEST_true(SSL_set_app_data(serverssl, clientssl)) -+ || !TEST_true(SSL_set_app_data(clientssl, serverssl)) -+ || !TEST_true(create_ssl_connection(serverssl, clientssl, -+ SSL_ERROR_NONE)) -+ || !TEST_true(SSL_version(serverssl) == TLS1_3_VERSION) -+ || !TEST_true(SSL_version(clientssl) == TLS1_3_VERSION) -+ || !(TEST_int_eq(SSL_quic_read_level(clientssl), ssl_encryption_application)) -+ || !(TEST_int_eq(SSL_quic_read_level(serverssl), ssl_encryption_application)) -+ || !(TEST_int_eq(SSL_quic_write_level(clientssl), ssl_encryption_application)) -+ || !(TEST_int_eq(SSL_quic_write_level(serverssl), ssl_encryption_application))) -+ goto end; -+ -+ SSL_get_peer_quic_transport_params(serverssl, &peer_str, &peer_str_len); -+ if (!TEST_mem_eq(peer_str, peer_str_len, client_str, sizeof(client_str))) -+ goto end; -+ SSL_get_peer_quic_transport_params(clientssl, &peer_str, &peer_str_len); -+ if (!TEST_mem_eq(peer_str, peer_str_len, server_str, sizeof(server_str))) -+ goto end; -+ -+ /* Deal with two NewSessionTickets */ -+ if (!TEST_true(SSL_process_quic_post_handshake(clientssl)) -+ || !TEST_true(SSL_process_quic_post_handshake(clientssl))) -+ goto end; -+ -+ testresult = 1; -+ -+end: -+ SSL_free(serverssl); -+ SSL_free(clientssl); -+ SSL_CTX_free(sctx); -+ SSL_CTX_free(cctx); -+ -+ return testresult; -+} -+#endif -+ - int setup_tests(void) - { - if (!TEST_ptr(certsdir = test_get_argument(0)) -@@ -6590,6 +6719,9 @@ int setup_tests(void) - ADD_ALL_TESTS(test_client_cert_cb, 2); - ADD_ALL_TESTS(test_ca_names, 3); - ADD_ALL_TESTS(test_servername, 10); -+#ifndef OPENSSL_NO_QUIC -+ ADD_TEST(test_quic_api); -+#endif - return 1; - } - -diff --git a/deps/openssl/openssl/test/ssltestlib.c b/deps/openssl/openssl/test/ssltestlib.c -index 456afdf471..5f61e63390 100644 ---- a/deps/openssl/openssl/test/ssltestlib.c -+++ b/deps/openssl/openssl/test/ssltestlib.c -@@ -917,6 +917,11 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want) - if (!create_bare_ssl_connection(serverssl, clientssl, want, 1)) - return 0; - -+#ifndef OPENSSL_NO_QUIC -+ /* QUIC does not support SSL_read_ex */ -+ if (SSL_is_quic(clientssl)) -+ return 1; -+#endif - /* - * We attempt to read some data on the client side which we expect to fail. - * This will ensure we have received the NewSessionTicket in TLSv1.3 where -diff --git a/deps/openssl/openssl/test/tls13secretstest.c b/deps/openssl/openssl/test/tls13secretstest.c -index 52fc2b6673..970c2f4aae 100644 ---- a/deps/openssl/openssl/test/tls13secretstest.c -+++ b/deps/openssl/openssl/test/tls13secretstest.c -@@ -216,6 +216,13 @@ int ossl_statem_export_early_allowed(SSL *s) - return 1; - } - -+#ifndef OPENSSL_NO_QUIC -+int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) -+{ -+ return 1; -+} -+#endif -+ - /* End of mocked out code */ - - static int test_secret(SSL *s, unsigned char *prk, -diff --git a/deps/openssl/openssl/util/libssl.num b/deps/openssl/openssl/util/libssl.num -index 297522c363..15785fe10d 100644 ---- a/deps/openssl/openssl/util/libssl.num -+++ b/deps/openssl/openssl/util/libssl.num -@@ -498,3 +498,14 @@ SSL_CTX_get_recv_max_early_data 498 1_1_1 EXIST::FUNCTION: - SSL_CTX_set_recv_max_early_data 499 1_1_1 EXIST::FUNCTION: - SSL_CTX_set_post_handshake_auth 500 1_1_1 EXIST::FUNCTION: - SSL_get_signature_type_nid 501 1_1_1a EXIST::FUNCTION: -+SSL_quic_read_level 10094 1_1_1d EXIST::FUNCTION:QUIC -+SSL_set_quic_transport_params 10095 1_1_1d EXIST::FUNCTION:QUIC -+SSL_CIPHER_get_prf_nid 10096 1_1_1d EXIST::FUNCTION: -+SSL_is_quic 10097 1_1_1d EXIST::FUNCTION:QUIC -+SSL_get_peer_quic_transport_params 10098 1_1_1d EXIST::FUNCTION:QUIC -+SSL_quic_write_level 10099 1_1_1d EXIST::FUNCTION:QUIC -+SSL_CTX_set_quic_method 10100 1_1_1d EXIST::FUNCTION:QUIC -+SSL_set_quic_method 10101 1_1_1d EXIST::FUNCTION:QUIC -+SSL_quic_max_handshake_flight_len 10102 1_1_1d EXIST::FUNCTION:QUIC -+SSL_process_quic_post_handshake 10103 1_1_1d EXIST::FUNCTION:QUIC -+SSL_provide_quic_data 10104 1_1_1d EXIST::FUNCTION:QUIC -diff --git a/deps/openssl/openssl/util/private.num b/deps/openssl/openssl/util/private.num -index bc7d967b5d..eb3d409f6e 100644 ---- a/deps/openssl/openssl/util/private.num -+++ b/deps/openssl/openssl/util/private.num -@@ -91,6 +91,8 @@ custom_ext_free_cb datatype - custom_ext_parse_cb datatype - pem_password_cb datatype - ssl_ct_validation_cb datatype -+OSSL_ENCRYPTION_LEVEL datatype -+SSL_QUIC_METHOD datatype - # - BIO_append_filename define - BIO_destroy_bio_pair define --- -2.26.0.windows.1 - diff --git a/doc/api/errors.md b/doc/api/errors.md index d0e0b8794f0..6f7b0188d86 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1899,37 +1899,6 @@ Accessing `Object.prototype.__proto__` has been forbidden using [`Object.setPrototypeOf`][] should be used to get and set the prototype of an object. -<a id="ERR_QUIC_FAILED_TO_CREATE_SESSION"></a> -### `ERR_QUIC_FAILED_TO_CREATE_SESSION` - -> Stability: 1 - Experimental - -An unspecified failure occured trying to initialize a new `QuicClientSession`. - -<a id="ERR_QUIC_INVALID_REMOTE_TRANSPORT_PARAMS"></a> -### `ERR_QUIC_INVALID_REMOTE_TRANSPORT_PARAMS` - -> Stability: 1 - Experimental - -An attempt to resume a `QuicClientSession` using remembered remote transport -parameters failed because the transport parameters were invalid. - -<a id="ERR_QUIC_INVALID_TLS_SESSION_TICKET"></a> -### `ERR_QUIC_INVALID_TLS_SESSION_TICKET` - -> Stability: 1 - Experimental - -An attempt resume a `QuicClientSession` using a remembered TLS session ticket -failed because the session ticket was invalid. - -<a id="ERR_QUIC_VERSION_NEGOTIATION"></a> -### `ERR_QUIC_VERSION_NEGOTIATION` - -> Stability: 1 - Experimental - -A `QuicClientSession` received a version negotiation request from the -server and was shutdown accordingly. - <a id="ERR_REQUIRE_ESM"></a> ### `ERR_REQUIRE_ESM` diff --git a/doc/api/index.md b/doc/api/index.md index 391873c1612..734d7f1cf05 100644 --- a/doc/api/index.md +++ b/doc/api/index.md @@ -47,7 +47,6 @@ * [Process](process.md) * [Punycode](punycode.md) * [Query strings](querystring.md) -* [QUIC](quic.md) * [Readline](readline.md) * [REPL](repl.md) * [Report](report.md) diff --git a/doc/api/net.md b/doc/api/net.md index d489070e240..fd7de2512cd 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -1237,14 +1237,6 @@ immediately initiates connection with [`socket.connect(port[, host][, connectListener])`][`socket.connect(port)`], then returns the `net.Socket` that starts the connection. -## `net.createQuicSocket([options])` -<!-- YAML -added: v15.0.0 ---> - -Creates and returns a new `QuicSocket`. Please refer to the [QUIC documentation][] -for details. - ## `net.createServer([options][, connectionListener])` <!-- YAML added: v0.5.0 @@ -1352,7 +1344,6 @@ Returns `true` if input is a version 6 IP address, otherwise returns `false`. [IPC]: #net_ipc_support [Identifying paths for IPC connections]: #net_identifying_paths_for_ipc_connections -[QUIC documentation]: quic.md [Readable Stream]: stream.md#stream_class_stream_readable [`'close'`]: #net_event_close [`'connect'`]: #net_event_connect diff --git a/doc/api/quic.md b/doc/api/quic.md deleted file mode 100644 index 78428f7c24e..00000000000 --- a/doc/api/quic.md +++ /dev/null @@ -1,2518 +0,0 @@ -# QUIC - -<!--introduced_in=v15.0.0--> - -> Stability: 1 - Experimental - -The `net` module provides an implementation of the QUIC protocol. To -access it, the Node.js binary must be compiled using the -`--experimental-quic` configuration flag. - -```js -const { createQuicSocket } = require('net'); -``` - -## Example - -```js -'use strict'; - -const key = getTLSKeySomehow(); -const cert = getTLSCertSomehow(); - -const { createQuicSocket } = require('net'); - -// Create the QUIC UDP IPv4 socket bound to local IP port 1234 -const socket = createQuicSocket({ endpoint: { port: 1234 } }); - -socket.on('session', async (session) => { - // A new server side session has been created! - - // The peer opened a new stream! - session.on('stream', (stream) => { - // Let's say hello - stream.end('Hello World'); - - // Let's see what the peer has to say... - stream.setEncoding('utf8'); - stream.on('data', console.log); - stream.on('end', () => console.log('stream ended')); - }); - - const uni = await session.openStream({ halfOpen: true }); - uni.write('hi '); - uni.end('from the server!'); -}); - -// Tell the socket to operate as a server using the given -// key and certificate to secure new connections, using -// the fictional 'hello' application protocol. -(async function() { - await socket.listen({ key, cert, alpn: 'hello' }); - console.log('The socket is listening for sessions!'); -})(); - -``` - -## QUIC basics - -QUIC is a UDP-based network transport protocol that includes built-in security -via TLS 1.3, flow control, error correction, connection migration, -multiplexing, and more. - -Within the Node.js implementation of the QUIC protocol, there are three main -components: the `QuicSocket`, the `QuicSession` and the `QuicStream`. - -### QuicSocket - -A `QuicSocket` encapsulates a binding to one or more local UDP ports. It is -used to send data to, and receive data from, remote endpoints. Once created, -a `QuicSocket` is associated with a local network address and IP port and can -act as both a QUIC client and server simultaneously. User code at the -JavaScript level interacts with the `QuicSocket` object to: - -* Query or modified the properties of the local UDP binding; -* Create client `QuicSession` instances; -* Wait for server `QuicSession` instances; or -* Query activity statistics - -Unlike the `net.Socket` and `tls.TLSSocket`, a `QuicSocket` instance cannot be -directly used by user code at the JavaScript level to send or receive data over -the network. - -### Client and server QuicSessions - -A `QuicSession` represents a logical connection between two QUIC endpoints (a -client and a server). In the JavaScript API, each is represented by the -`QuicClientSession` and `QuicServerSession` specializations. - -At any given time, a `QuicSession` exists is one of four possible states: - -* `Initial` - Entered as soon as the `QuicSession` is created. -* `Handshake` - Entered as soon as the TLS 1.3 handshake between the client and - server begins. The handshake is always initiated by the client. -* `Ready` - Entered as soon as the TLS 1.3 handshake completes. Once the - `QuicSession` enters the `Ready` state, it may be used to exchange - application data using `QuicStream` instances. -* `Closed` - Entered as soon as the `QuicSession` connection has been - terminated. - -New instances of `QuicClientSession` are created using the `connect()` -function on a `QuicSocket` as in the example below: - -```js -const { createQuicSocket } = require('net'); - -// Create a QuicSocket associated with localhost and port 1234 -const socket = createQuicSocket({ endpoint: { port: 1234 } }); - -(async function() { - const client = await socket.connect({ - address: 'example.com', - port: 4567, - alpn: 'foo' - }); -})(); -``` - -As soon as the `QuicClientSession` is created, the `address` provided in -the connect options will be resolved to an IP address (if necessary), and -the TLS 1.3 handshake will begin. The `QuicClientSession` cannot be used -to exchange application data until after the `'secure'` event has been -emitted by the `QuicClientSession` object, signaling the completion of -the TLS 1.3 handshake. - -```js -client.on('secure', () => { - // The QuicClientSession can now be used for application data -}); -``` - -New instances of `QuicServerSession` are created internally by the -`QuicSocket` if it has been configured to listen for new connections -using the `listen()` method. - -```js -const { createQuicSocket } = require('net'); - -const key = getTLSKeySomehow(); -const cert = getTLSCertSomehow(); - -const socket = createQuicSocket(); - -socket.on('session', (session) => { - session.on('secure', () => { - // The QuicServerSession can now be used for application data - }); -}); - -(async function() { - await socket.listen({ key, cert, alpn: 'foo' }); -})(); -``` - -As with client `QuicSession` instances, the `QuicServerSession` cannot be -used to exchange application data until the `'secure'` event has been emitted. - -### QuicSession and ALPN - -QUIC uses the TLS 1.3 [ALPN][] ("Application-Layer Protocol Negotiation") -extension to identify the application level protocol that is using the QUIC -connection. Every `QuicSession` instance has an ALPN identifier that *must* be -specified in either the `connect()` or `listen()` options. ALPN identifiers that -are known to Node.js (such as the ALPN identifier for HTTP/3) will alter how the -`QuicSession` and `QuicStream` objects operate internally, but the QUIC -implementation for Node.js has been designed to allow any ALPN to be specified -and used. - -### QuicStream - -Once a `QuicSession` transitions to the `Ready` state, `QuicStream` instances -may be created and used to exchange application data. On a general level, all -`QuicStream` instances are simply Node.js Duplex Streams that allow -bidirectional data flow between the QUIC client and server. However, the -application protocol negotiated for the `QuicSession` may alter the semantics -and operation of a `QuicStream` associated with the session. Specifically, -some features of the `QuicStream` (e.g. headers) are enabled only if the -application protocol selected is known by Node.js to support those features. - -Once the `QuicSession` is ready, a `QuicStream` may be created by either the -client or server, and may be unidirectional or bidirectional. - -The `openStream()` method is used to create a new `QuicStream`: - -```js -// Create a new bidirectional stream -async function createStreams(session) { - const stream1 = await session.openStream(); - - // Create a new unidirectional stream - const stream2 = await session.openStream({ halfOpen: true }); -} -``` - -As suggested by the names, a bidirectional stream allows data to be sent on -a stream in both directions, by both client and server, regardless of which -peer opened the stream. A unidirectional stream can be written to only by the -QuicSession that opened it. - -The `'stream'` event is emitted by the `QuicSession` when a new `QuicStream` -has been initiated by the connected peer: - -```js -session.on('stream', (stream) => { - if (stream.bidirectional) { - stream.write('Hello World'); - stream.end(); - } - stream.on('data', console.log); - stream.on('end', () => {}); -}); -``` - -#### QuicStream headers - -Some QUIC application protocols (like HTTP/3) use headers. - -There are four kinds of headers that the Node.js QUIC implementation -is capable of handling dependent entirely on known application protocol -support: - -* Informational Headers -* Initial Headers -* Trailing Headers -* Push Headers - -These categories correlate exactly with the equivalent HTTP -concepts: - -* Informational Headers: Any response headers transmitted within - a block of headers using a `1xx` status code. -* Initial Headers: HTTP request or response headers -* Trailing Headers: A block of headers that follow the body of a - request or response. -* Push Promise Headers: A block of headers included in a promised - push stream. - -If headers are supported by the application protocol in use for -a given `QuicSession`, the `'initialHeaders'`, `'informationalHeaders'`, -and `'trailingHeaders'` events will be emitted by the `QuicStream` -object when headers are received; and the `submitInformationalHeaders()`, -`submitInitialHeaders()`, and `submitTrailingHeaders()` methods can be -used to send headers. - -## QUIC and HTTP/3 - -HTTP/3 is an application layer protocol that uses QUIC as the transport. - -TBD - -## QUIC JavaScript API - -### `net.createQuicSocket([options])` -<!-- YAML -added: v15.0.0 ---> - -* `options` {Object} - * `client` {Object} A default configuration for QUIC client sessions created - using `quicsocket.connect()`. - * `disableStatelessReset` {boolean} When `true` the `QuicSocket` will not - send stateless resets. **Default**: `false`. - * `endpoint` {Object} An object describing the local address to bind to. - * `address` {string} The local address to bind to. This may be an IPv4 or - IPv6 address or a host name. If a host name is given, it will be resolved - to an IP address. - * `port` {number} The local port to bind to. - * `type` {string} Can be one of `'udp4'`, `'upd6'`, or `'udp6-only'` to - use IPv4, IPv6, or IPv6 with dual-stack mode disabled. - **Default**: `'udp4'`. - * `lookup` {Function} A [custom DNS lookup function][]. - **Default**: undefined. - * `maxConnections` {number} The maximum number of total active inbound - connections. - * `maxConnectionsPerHost` {number} The maximum number of inbound connections - allowed per remote host. Default: `100`. - * `maxStatelessResetsPerHost` {number} The maximum number of stateless - resets that the `QuicSocket` is permitted to send per remote host. - Default: `10`. - * `qlog` {boolean} Whether to enable ['qlog'][] for incoming sessions. - (For outgoing client sessions, set `client.qlog`.) Default: `false`. - * `retryTokenTimeout` {number} The maximum number of *seconds* for retry token - validation. Default: `10` seconds. - * `server` {Object} A default configuration for QUIC server sessions. - * `statelessResetSecret` {Buffer|Uint8Array} A 16-byte `Buffer` or - `Uint8Array` providing the secret to use when generating stateless reset - tokens. If not specified, a random secret will be generated for the - `QuicSocket`. **Default**: `undefined`. - * `validateAddress` {boolean} When `true`, the `QuicSocket` will use explicit - address validation using a QUIC `RETRY` frame when listening for new server - sessions. Default: `false`. - -The `net.createQuicSocket()` function is used to create new `QuicSocket` -instances associated with a local UDP address. - -### Class: `QuicEndpoint` -<!-- YAML -added: v15.0.0 ---> - -The `QuicEndpoint` wraps a local UDP binding used by a `QuicSocket` to send -and receive data. A single `QuicSocket` may be bound to multiple -`QuicEndpoint` instances at any given time. - -Users will not create instances of `QuicEndpoint` directly. - -#### `quicendpoint.addMembership(address, iface)` -<!-- YAML -added: v15.0.0 ---> - -* `address` {string} -* `iface` {string} - -Tells the kernel to join a multicast group at the given `multicastAddress` and -`multicastInterface` using the `IP_ADD_MEMBERSHIP` socket option. If the -`multicastInterface` argument is not specified, the operating system will -choose one interface and will add membership to it. To add membership to every -available interface, call `addMembership()` multiple times, once per -interface. - -#### `quicendpoint.address` -<!-- YAML -added: v15.0.0 ---> - -* Type: Address - -An object containing the address information for a bound `QuicEndpoint`. - -The object will contain the properties: - -* `address` {string} The local IPv4 or IPv6 address to which the `QuicEndpoint` is - bound. -* `family` {string} Either `'IPv4'` or `'IPv6'`. -* `port` {number} The local IP port to which the `QuicEndpoint` is bound. - -If the `QuicEndpoint` is not bound, `quicendpoint.address` is an empty object. - -#### `quicendpoint.bind([options])` -<!-- YAML -added: v15.0.0 ---> - -Binds the `QuicEndpoint` if it has not already been bound. User code will -not typically be responsible for binding a `QuicEndpoint` as the owning -`QuicSocket` will do that automatically. - -* `options` {Object} - * `signal` {AbortSignal} Optionally allows the `bind()` to be canceled - using an `AbortController`. -* Returns: {Promise} - -The `quicendpoint.bind()` function returns `Promise` that will be resolved -with the address once the bind operation is successful. - -If the `QuicEndpoint` has been destroyed, or is destroyed while the `Promise` -is pending, the `Promise` will be rejected with an `ERR_INVALID_STATE` error. - -If an `AbortSignal` is specified in the `options` and it is triggered while -the `Promise` is pending, the `Promise` will be rejected with an `AbortError`. - -If `quicendpoint.bind()` is called again while a previously returned `Promise` -is still pending or has already successfully resolved, the previously returned -pending `Promise` will be returned. If the additional call to -`quicendpoint.bind()` contains an `AbortSignal`, the `signal` will be ignored. - -#### `quicendpoint.bound` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicEndpoint` is bound to the local UDP port. - -#### `quicendpoint.close()` -<!-- YAML -added: v15.0.0 ---> - -Closes and destroys the `QuicEndpoint`. Returns a `Promise` that is resolved -once the `QuicEndpoint` has been destroyed, or rejects if the `QuicEndpoint` -is destroyed with an error. - -* Returns: {Promise} - -The `Promise` cannot be canceled. Once `quicendpoint.close()` is called, the -`QuicEndpoint` will be destroyed. - -#### `quicendpoint.closing` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicEndpoint` is in the process of closing. - -#### `quicendpoint.destroy([error])` -<!-- YAML -added: v15.0.0 ---> - -* `error` {Object} An `Error` object. - -Closes and destroys the `QuicEndpoint` instance making it unusable. - -#### `quicendpoint.destroyed` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicEndpoint` has been destroyed. - -#### `quicendpoint.dropMembership(address, iface)` -<!-- YAML -added: v15.0.0 ---> - -* `address` {string} -* `iface` {string} - -Instructs the kernel to leave a multicast group at `multicastAddress` using the -`IP_DROP_MEMBERSHIP` socket option. This method is automatically called by the -kernel when the socket is closed or the process terminates, so most apps will -never have reason to call this. - -If `multicastInterface` is not specified, the operating system will attempt to -drop membership on all valid interfaces. - -#### `quicendpoint.fd` -<!-- YAML -added: v15.0.0 ---> - -* Type: {integer} - -The system file descriptor the `QuicEndpoint` is bound to. This property -is not set on Windows. - -#### `quicendpoint.pending` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicEndpoint` is in the process of binding to -the local UDP port. - -#### `quicendpoint.ref()` -<!-- YAML -added: v15.0.0 ---> - -#### `quicendpoint.setBroadcast([on])` -<!-- YAML -added: v15.0.0 ---> - -* `on` {boolean} - -Sets or clears the `SO_BROADCAST` socket option. When set to `true`, UDP -packets may be sent to a local interface's broadcast address. - -#### `quicendpoint.setMulticastInterface(iface)` -<!-- YAML -added: v15.0.0 ---> - -* `iface` {string} - -All references to scope in this section are referring to IPv6 Zone Indices, -which are defined by [RFC 4007][]. In string form, an IP with a scope index -is written as `'IP%scope'` where scope is an interface name or interface -number. - -Sets the default outgoing multicast interface of the socket to a chosen -interface or back to system interface selection. The multicastInterface must -be a valid string representation of an IP from the socket's family. - -For IPv4 sockets, this should be the IP configured for the desired physical -interface. All packets sent to multicast on the socket will be sent on the -interface determined by the most recent successful use of this call. - -For IPv6 sockets, multicastInterface should include a scope to indicate the -interface as in the examples that follow. In IPv6, individual send calls can -also use explicit scope in addresses, so only packets sent to a multicast -address without specifying an explicit scope are affected by the most recent -successful use of this call. - -##### Examples: IPv6 outgoing multicast interface -<!-- YAML -added: v15.0.0 ---> -On most systems, where scope format uses the interface name: - -```js -const { createQuicSocket } = require('net'); -const socket = createQuicSocket({ endpoint: { type: 'udp6', port: 1234 } }); - -socket.on('ready', () => { - socket.endpoints[0].setMulticastInterface('::%eth1'); -}); -``` - -On Windows, where scope format uses an interface number: - -```js -const { createQuicSocket } = require('net'); -const socket = createQuicSocket({ endpoint: { type: 'udp6', port: 1234 } }); - -socket.on('ready', () => { - socket.endpoints[0].setMulticastInterface('::%2'); -}); -``` - -##### Example: IPv4 outgoing multicast interface -<!-- YAML -added: v15.0.0 ---> -All systems use an IP of the host on the desired physical interface: - -```js -const { createQuicSocket } = require('net'); -const socket = createQuicSocket({ endpoint: { type: 'udp4', port: 1234 } }); - -socket.on('ready', () => { - socket.endpoints[0].setMulticastInterface('10.0.0.2'); -}); -``` - -##### Call results - -A call on a socket that is not ready to send or no longer open may throw a -Not running Error. - -If multicastInterface can not be parsed into an IP then an `EINVAL` System -Error is thrown. - -On IPv4, if `multicastInterface` is a valid address but does not match any -interface, or if the address does not match the family then a System Error -such as `EADDRNOTAVAIL` or `EPROTONOSUP` is thrown. - -On IPv6, most errors with specifying or omitting scope will result in the -socket continuing to use (or returning to) the system's default interface -selection. - -A socket's address family's ANY address (IPv4 `'0.0.0.0'` or IPv6 `'::'`) -can be used to return control of the sockets default outgoing interface to -the system for future multicast packets. - -#### `quicendpoint.setMulticastLoopback([on])` -<!-- YAML -added: v15.0.0 ---> - -* `on` {boolean} - -Sets or clears the `IP_MULTICAST_LOOP` socket option. When set to `true`, -multicast packets will also be received on the local interface. - -#### `quicendpoint.setMulticastTTL(ttl)` -<!-- YAML -added: v15.0.0 ---> - -* `ttl` {number} - -Sets the `IP_MULTICAST_TTL` socket option. While TTL generally stands for -"Time to Live", in this context it specifies the number of IP hops that a -packet is allowed to travel through, specifically for multicast traffic. Each -router or gateway that forwards a packet decrements the TTL. If the TTL is -decremented to `0` by a router, it will not be forwarded. - -The argument passed to `setMulticastTTL()` is a number of hops between -`0` and `255`. The default on most systems is `1` but can vary. - -#### `quicendpoint.setTTL(ttl)` -<!-- YAML -added: v15.0.0 ---> - -* `ttl` {number} - -Sets the `IP_TTL` socket option. While TTL generally stands for "Time to Live", -in this context it specifies the number of IP hops that a packet is allowed to -travel through. Each router or gateway that forwards a packet decrements the -TTL. If the TTL is decremented to `0` by a router, it will not be forwarded. -Changing TTL values is typically done for network probes or when multicasting. - -The argument to `setTTL()` is a number of hops between `1` and `255`. -The default on most systems is `64` but can vary. - -#### `quicendpoint.unref()` -<!-- YAML -added: v15.0.0 ---> - -### Class: `QuicSession extends EventEmitter` -<!-- YAML -added: v15.0.0 ---> -* Extends: {EventEmitter} - -The `QuicSession` is an abstract base class that defines events, methods, and -properties that are shared by both `QuicClientSession` and `QuicServerSession`. - -Users will not create instances of `QuicSession` directly. - -#### Event: `'close'` -<!-- YAML -added: v15.0.0 ---> - -Emitted after the `QuicSession` has been destroyed and is no longer usable. - -The `'close'` event will not be emitted more than once. - -#### Event: `'error'` -<!-- YAML -added: v15.0.0 ---> - -Emitted immediately before the `'close'` event if the `QuicSession` was -destroyed with an error. - -The callback will be invoked with a single argument: - -* `error` {Object} An `Error` object. - -The `'error'` event will not be emitted more than once. - -#### Event: `'keylog'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when key material is generated or received by a `QuicSession` -(typically during or immediately following the handshake process). This keying -material can be stored for debugging, as it allows captured TLS traffic to be -decrypted. It may be emitted multiple times per `QuicSession` instance. - -The callback will be invoked with a single argument: - -* `line` {Buffer} Line of ASCII text, in NSS SSLKEYLOGFILE format. - -A typical use case is to append received lines to a common text file, which is -later used by software (such as Wireshark) to decrypt the traffic: - -```js -const log = fs.createWriteStream('/tmp/ssl-keys.log', { flags: 'a' }); -// ... -session.on('keylog', (line) => log.write(line)); -``` - -The `'keylog'` event will be emitted multiple times. - -#### Event: `'pathValidation'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when a path validation result has been determined. This event -is strictly informational. When path validation is successful, the -`QuicSession` will automatically update to use the new validated path. - -The callback will be invoked with three arguments: - -* `result` {string} Either `'failure'` or `'success'`, denoting the status - of the path challenge. -* `local` {Object} The local address component of the tested path. -* `remote` {Object} The remote address component of the tested path. - -The `'pathValidation'` event will be emitted multiple times. - -#### Event: `'secure'` -<!-- YAML -added: v15.0.0 ---> - -Emitted after the TLS handshake has been completed. - -The callback will be invoked with two arguments: - -* `servername` {string} The SNI servername requested by the client. -* `alpnProtocol` {string} The negotiated ALPN protocol. -* `cipher` {Object} Information about the selected cipher algorithm. - * `name` {string} The cipher algorithm name. - * `version` {string} The TLS version (currently always `'TLSv1.3'`). - -These will also be available using the `quicsession.servername`, -`quicsession.alpnProtocol`, and `quicsession.cipher` properties. - -The `'secure'` event will not be emitted more than once. - -#### Event: `'stream'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when a new `QuicStream` has been initiated by the connected peer. - -The `'stream'` event may be emitted multiple times. - -#### `quicsession.ackDelayRetransmitCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of retransmissions caused by delayed acknowledgments. - -#### `quicsession.address` -<!-- YAML -added: v15.0.0 ---> - -* Type: {Object} - * `address` {string} The local IPv4 or IPv6 address to which the `QuicSession` - is bound. - * `family` {string} Either `'IPv4'` or `'IPv6'`. - * `port` {number} The local IP port to which the `QuicSocket` is bound. - -An object containing the local address information for the `QuicSocket` to which -the `QuicSession` is currently associated. - -#### `quicsession.alpnProtocol` -<!-- YAML -added: v15.0.0 ---> - -* Type: {string} - -The ALPN protocol identifier negotiated for this session. - -#### `quicsession.authenticated` -<!--YAML -added: v15.0.0 ---> -* Type: {boolean} - -True if the certificate provided by the peer during the TLS 1.3 -handshake has been verified. - -#### `quicsession.authenticationError` - -* Type: {Object} An error object - -If `quicsession.authenticated` is false, returns an `Error` object -representing the reason the peer certificate verification failed. - -#### `quicsession.bidiStreamCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of bidirectional streams created for this `QuicSession`. - -#### `quicsession.blockCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of times the `QuicSession` has been blocked from sending -stream data due to flow control. - -Such blocks indicate that transmitted stream data is not being consumed -quickly enough by the connected peer. - -#### `quicsession.bytesInFlight` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of unacknowledged bytes this QUIC endpoint has transmitted -to the connected peer. - -#### `quicsession.bytesReceived` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of bytes received from the peer. - -#### `quicsession.bytesSent` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of bytes sent to the peer. - -#### `quicsession.cipher` -<!-- YAML -added: v15.0.0 ---> - -* Type: {Object} - * `name` {string} The cipher algorithm name. - * `type` {string} The TLS version (currently always `'TLSv1.3'`). - -Information about the cipher algorithm selected for the session. - -#### `quicsession.close()` -<!-- YAML -added: v15.0.0 ---> - -Begins a graceful close of the `QuicSession`. Existing `QuicStream` instances -will be permitted to close naturally. New `QuicStream` instances will not be -permitted. Once all `QuicStream` instances have closed, the `QuicSession` -instance will be destroyed. Returns a `Promise` that is resolved once the -`QuicSession` instance is destroyed. - -#### `quicsession.closeCode` -<!-- YAML -added: v15.0.0 ---> -* Type: {Object} - * `code` {number} The error code reported when the `QuicSession` closed. - * `family` {number} The type of error code reported (`0` indicates a QUIC - protocol level error, `1` indicates a TLS error, `2` represents an - application level error.) - -#### `quicsession.closing` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicSession` is in the process of a graceful shutdown. - -#### `quicsession.destroy([error])` -<!-- YAML -added: v15.0.0 ---> - -* `error` {any} - -Destroys the `QuicSession` immediately causing the `close` event to be emitted. -If `error` is not `undefined`, the `error` event will be emitted immediately -before the `close` event. - -Any `QuicStream` instances that are still opened will be abruptly closed. - -#### `quicsession.destroyed` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicSession` has been destroyed. - -#### `quicsession.duration` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The length of time the `QuicSession` was active. - -#### `quicsession.getCertificate()` -<!-- YAML -added: v15.0.0 ---> - -* Returns: {Object} A [Certificate Object][]. - -Returns an object representing the *local* certificate. The returned object has -some properties corresponding to the fields of the certificate. - -If there is no local certificate, or if the `QuicSession` has been destroyed, -an empty object will be returned. - -#### `quicsession.getPeerCertificate([detailed])` -<!-- YAML -added: v15.0.0 ---> - -* `detailed` {boolean} Include the full certificate chain if `true`, otherwise - include just the peer's certificate. **Default**: `false`. -* Returns: {Object} A [Certificate Object][]. - -Returns an object representing the peer's certificate. If the peer does not -provide a certificate, or if the `QuicSession` has been destroyed, an empty -object will be returned. - -If the full certificate chain was requested (`details` equals `true`), each -certificate will include an `issuerCertificate` property containing an object -representing the issuer's certificate. - -#### `quicsession.handshakeAckHistogram` -<!-- YAML -added: v15.0.0 ---> - -TBD - -#### `quicsession.handshakeContinuationHistogram` -<!-- YAML -added: v15.0.0 ---> - -TBD - -#### `quicsession.handshakeComplete` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the TLS handshake has completed. - -#### `quicsession.handshakeConfirmed` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` when the TLS handshake completion has been confirmed. - -#### `quicsession.handshakeDuration` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The length of time taken to complete the TLS handshake. - -#### `quicsession.idleTimeout` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicSession` was closed due to an idle timeout. - -#### `quicsession.keyUpdateCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of key update operations that have occurred. - -#### `quicsession.latestRTT` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The most recently recorded RTT for this `QuicSession`. - -#### `quicsession.lossRetransmitCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of lost-packet retransmissions that have been performed on -this `QuicSession`. - -#### `quicsession.maxDataLeft` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of bytes the `QuicSession` is *currently* allowed to -send to the connected peer. - -#### `quicsession.maxInFlightBytes` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The maximum number of in-flight bytes recorded for this `QuicSession`. - -#### `quicsession.maxStreams` -<!-- YAML -added: v15.0.0 ---> - -* Type: {Object} - * `uni` {number} The maximum number of unidirectional streams. - * `bidi` {number} The maximum number of bidirectional streams. - -The highest cumulative number of bidirectional and unidirectional streams -that can currently be opened. The values are set initially by configuration -parameters when the `QuicSession` is created, then updated over the lifespan -of the `QuicSession` as the connected peer allows new streams to be created. - -#### `quicsession.minRTT` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The minimum RTT recorded so far for this `QuicSession`. - -#### `quicsession.openStream([options])` -<!-- YAML -added: v15.0.0 ---> -* `options` {Object} - * `halfOpen` {boolean} Set to `true` to open a unidirectional stream, `false` - to open a bidirectional stream. **Default**: `true`. - * `highWaterMark` {number} Total number of bytes that the `QuicStream` may - buffer internally before the `quicstream.write()` function starts returning - `false`. Default: `16384`. - * `defaultEncoding` {string} The default encoding that is used when no - encoding is specified as an argument to `quicstream.write()`. Default: - `'utf8'`. -* Returns: {Promise} containing {QuicStream} - -Returns a `Promise` that resolves a new `QuicStream`. - -The `Promise` will be rejected if the `QuicSession` has been destroyed, is in -the process of a graceful shutdown, or the `QuicSession` is otherwise blocked -from opening a new stream. - -#### `quicsession.ping()` -<!--YAML -added: v15.0.0 ---> - -The `ping()` method will trigger the underlying QUIC connection to serialize -any frames currently pending in the outbound queue if it is able to do so. -This has the effect of keeping the connection with the peer active and resets -the idle and retransmission timers. The `ping()` method is a best-effort -that ignores any errors that may occur during the serialization and send -operations. There is no return value and there is no way to monitor the status -of the `ping()` operation. - -#### `quicsession.peerInitiatedStreamCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of `QuicStreams` initiated by the connected peer. - -#### `quicsession.qlog` -<!-- YAML -added: v15.0.0 ---> - -* Type: {stream.Readable} - -If `qlog` support is enabled for `QuicSession`, the `quicsession.qlog` property -provides a [`stream.Readable`][] that may be used to access the `qlog` event -data according to the [qlog standard][]. For client `QuicSessions`, the -`quicsession.qlog` property will be `undefined` until the `'qlog'` event -is emitted. - -#### `quicsession.remoteAddress` -<!-- YAML -added: v15.0.0 ---> - -* Type: {Object} - * `address` {string} The local IPv4 or IPv6 address to which the `QuicSession` - is connected. - * `family` {string} Either `'IPv4'` or `'IPv6'`. - * `port` {number} The local IP port to which the `QuicSocket` is bound. - -An object containing the remote address information for the connected peer. - -#### `quicsession.selfInitiatedStreamCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of `QuicStream` instances initiated by this `QuicSession`. - -#### `quicsession.servername` -<!-- YAML -added: v15.0.0 ---> - -* Type: {string} - -The SNI servername requested for this session by the client. - -#### `quicsession.smoothedRTT` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The modified RTT calculated for this `QuicSession`. - -#### `quicsession.socket` -<!-- YAML -added: v15.0.0 ---> - -* Type: {QuicSocket} - -The `QuicSocket` the `QuicSession` is associated with. - -#### `quicsession.statelessReset` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -True if the `QuicSession` was closed due to QUIC stateless reset. - -#### `quicsession.uniStreamCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of unidirectional streams created on this `QuicSession`. - -#### `quicsession.updateKey()` -<!-- YAML -added: v15.0.0 ---> - -* Returns: {boolean} `true` if the key update operation is successfully - initiated. - -Initiates QuicSession key update. - -An error will be thrown if called before `quicsession.handshakeConfirmed` -is equal to `true`. - -#### `quicsession.usingEarlyData` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -On server `QuicSession` instances, set to `true` on completion of the TLS -handshake if early data is enabled. On client `QuicSession` instances, -set to true on handshake completion if early data is enabled *and* was -accepted by the server. - -### Class: `QuicClientSession extends QuicSession` -<!-- YAML -added: v15.0.0 ---> - -* Extends: {QuicSession} - -The `QuicClientSession` class implements the client side of a QUIC connection. -Instances are created using the `quicsocket.connect()` method. - -#### Event: `'sessionTicket'` -<!-- YAML -added: v15.0.0 ---> - -The `'sessionTicket'` event is emitted when a new TLS session ticket has been -generated for the current `QuicClientSession`. The callback is invoked with -two arguments: - -* `sessionTicket` {Buffer} The serialized session ticket. -* `remoteTransportParams` {Buffer} The serialized remote transport parameters - provided by the QUIC server. - -The `sessionTicket` and `remoteTransportParams` are useful when creating a new -`QuicClientSession` to more quickly resume an existing session. - -The `'sessionTicket'` event may be emitted multiple times. - -#### Event: `'qlog'` -<!-- YAML -added: v15.0.0 ---> - -The `'qlog'` event is emitted when the `QuicClientSession` is ready to begin -providing `qlog` event data. The callback is invoked with a single argument: - -* `qlog` {stream.Readable} A [`stream.Readable`][] that is also available using - the `quicsession.qlog` property. - -#### Event: `'usePreferredAddress'` -<!-- YAML -added: v15.0.0 ---> - -The `'usePreferredAddress'` event is emitted when the client `QuicSession` -is updated to use the server-advertised preferred address. The callback is -invoked with a single `address` argument: - -* `address` {Object} - * `address` {string} The preferred host name - * `port` {number} The preferred IP port - * `type` {string} Either `'udp4'` or `'udp6'`. - -This event is purely informational and will be emitted only when -`preferredAddressPolicy` is set to `'accept'`. - -The `'usePreferredAddress'` event will not be emitted more than once. - -#### `quicclientsession.ephemeralKeyInfo` -<!-- YAML -added: v15.0.0 ---> - -* Type: {Object} - -An object representing the type, name, and size of parameter of an ephemeral -key exchange in Perfect Forward Secrecy on a client connection. It is an -empty object when the key exchange is not ephemeral. The supported types are -`'DH'` and `'ECDH'`. The `name` property is available only when type is -`'ECDH'`. - -For example: `{ type: 'ECDH', name: 'prime256v1', size: 256 }`. - -#### `quicclientsession.setSocket(socket[, natRebinding])` -<!-- YAML -added: v15.0.0 ---> - -* `socket` {QuicSocket} A `QuicSocket` instance to move this session to. -* `natRebinding` {boolean} When `true`, indicates that the local address is to - be changed without triggering address validation. This will be rare and will - typically be used only to test resiliency in NAT rebind scenarios. - **Default**: `false`. -* Returns: {Promise} - -Migrates the `QuicClientSession` to the given `QuicSocket` instance. If the new -`QuicSocket` has not yet been bound to a local UDP port, it will be bound prior -to attempting the migration. - -### Class: `QuicServerSession extends QuicSession` -<!-- YAML -added: v15.0.0 ---> - -* Extends: {QuicSession} - -The `QuicServerSession` class implements the server side of a QUIC connection. -Instances are created internally and are emitted using the `QuicSocket` -`'session'` event. - -### Class: `QuicSocket` -<!-- YAML -added: v15.0.0 ---> - -New instances of `QuicSocket` are created using the `net.createQuicSocket()` -method, and can be used as both a client and a server. - -#### Event: `'busy'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when the server busy state has been toggled using -`quicSocket.serverBusy = true | false`. The callback is invoked with no -arguments. Use the `quicsocket.serverBusy` property to determine the -current status. This event is strictly informational. - -```js -const { createQuicSocket } = require('net'); - -const socket = createQuicSocket(); - -socket.on('busy', () => { - if (socket.serverBusy) - console.log('Server is busy'); - else - console.log('Server is not busy'); -}); - -socket.serverBusy = true; -socket.serverBusy = false; -``` - -This `'busy'` event may be emitted multiple times. - -#### Event: `'close'` -<!-- YAML -added: v15.0.0 ---> - -Emitted after the `QuicSocket` has been destroyed and is no longer usable. - -The `'close'` event will only ever be emitted once. - -#### Event: `'endpointClose'` -<!-- YAML -added: v15.0.0 ---> - -Emitted after a `QuicEndpoint` associated with the `QuicSocket` closes and -has been destroyed. The handler will be invoked with two arguments: - -* `endpoint` {QuicEndpoint} The `QuicEndpoint` that has been destroyed. -* `error` {Error} An `Error` object if the `QuicEndpoint` was destroyed because - of an error. - -When all of the `QuicEndpoint` instances associated with a `QuicSocket` have -closed, the `QuicEndpoint` will also automatically close. - -#### Event: `'error'` -<!-- YAML -added: v15.0.0 ---> - -Emitted before the `'close'` event if the `QuicSocket` was destroyed with an -`error`. - -The `'error'` event will only ever be emitted once. - -#### Event: `'listening'` -<!-- YAML -added: v15.0.0 ---> - -Emitted after `quicsocket.listen()` is called and the `QuicSocket` has started -listening for incoming `QuicServerSession`s. The callback is invoked with -no arguments. - -The `'listening'` event will only ever be emitted once. - -#### Event: `'ready'` -<!-- YAML -added: v15.0.0 ---> - -Emitted once the `QuicSocket` has been bound to a local UDP port. - -The `'ready'` event will only ever be emitted once. - -#### Event: `'session'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when a new `QuicServerSession` has been created. The callback is -invoked with a single argument providing the newly created `QuicServerSession` -object. - -```js -const { createQuicSocket } = require('net'); - -const options = getOptionsSomehow(); -const server = createQuicSocket({ server: options }); - -server.on('session', (session) => { - // Attach session event listeners. -}); - -server.listen(); -``` - -The `'session'` event will be emitted multiple times. - -The `'session'` event handler can be an async function. - -If the `'session'` event handler throws an error, or if it returns a `Promise` -that is rejected, the error will be handled by destroying the `QuicServerSession` -automatically and emitting a `'sessionError'` event on the `QuicSocket`. - -#### Event: `'sessionError'` -<!--YAML -added: v15.0.0 ---> - -Emitted when an error occurs processing an event related to a specific -`QuicSession` instance. The callback is invoked with two arguments: - -* `error` {Error} The error that was either thrown or rejected. -* `session` {QuicSession} The `QuicSession` instance that was destroyed. - -The `QuicSession` instance will have been destroyed by the time the -`'sessionError'` event is emitted. - -```js -const { createQuicSocket } = require('net'); - -const options = getOptionsSomehow(); -const server = createQuicSocket({ server: options }); -server.listen(); - -server.on('session', (session) => { - throw new Error('boom'); -}); - -server.on('sessionError', (error, session) => { - console.log('error:', error.message); -}); -``` - -#### `quicsocket.addEndpoint(options)` -<!-- YAML -added: v15.0.0 ---> - -* `options`: {Object} An object describing the local address to bind to. - * `address` {string} The local address to bind to. This may be an IPv4 or - IPv6 address or a host name. If a host name is given, it will be resolved - to an IP address. - * `port` {number} The local port to bind to. - * `type` {string} Can be one of `'udp4'`, `'upd6'`, or `'udp6-only'` to - use IPv4, IPv6, or IPv6 with dual-stack mode disabled. - **Default**: `'udp4'`. - * `lookup` {Function} A [custom DNS lookup function][]. - **Default**: undefined. -* Returns: {QuicEndpoint} - -Creates and adds a new `QuicEndpoint` to the `QuicSocket` instance. An -error will be thrown if `quicsocket.addEndpoint()` is called either after -the `QuicSocket` has already started binding to the local ports, or after -the `QuicSocket` has been destroyed. - -#### `quicsocket.blockList` -<!-- YAML -added: v15.0.0 ---> - -* Type: {net.BlockList} - -A {net.BlockList} instance used to define rules for remote IPv4 or IPv6 -addresses that this `QuicSocket` is not permitted to interact with. The -rules can be specified as either specific individual addresses, ranges -of addresses, or CIDR subnet ranges. - -When listening as a server, if a packet is received from a blocked address, -the packet will be ignored. - -When connecting as a client, if the remote IP address is blocked, the -connection attempt will be rejected. - -#### `quicsocket.bound` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Will be `true` if the `QuicSocket` has been successfully bound to a local UDP -port. Initially the value is `false`. - -`QuicSocket` instances are not bound to a local UDP port until the first time -either `quicsocket.listen()` or `quicsocket.connect()` is called. The `'ready'` -event will be emitted once the `QuicSocket` has been bound and the value of -`quicsocket.bound` will become `true`. - -Read-only. - -#### `quicsocket.boundDuration` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The length of time this `QuicSocket` has been bound to a local port. - -Read-only. - -#### `quicsocket.bytesReceived` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of bytes received by this `QuicSocket`. - -Read-only. - -#### `quicsocket.bytesSent` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of bytes sent by this `QuicSocket`. - -Read-only. - -#### `quicsocket.clientSessions` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of client `QuicSession` instances that have been associated -with this `QuicSocket`. - -Read-only. - -#### `quicsocket.close()` -<!-- YAML -added: v15.0.0 ---> - -* Returns: {Promise} - -Gracefully closes the `QuicSocket`. Existing `QuicSession` instances will be -permitted to close naturally. New `QuicClientSession` and `QuicServerSession` -instances will not be allowed. The returns `Promise` will be resolved once -the `QuicSocket` is destroyed. - -#### `quicsocket.connect([options])` -<!-- YAML -added: v15.0.0 ---> - -* `options` {Object} - * `address` {string} The domain name or IP address of the QUIC server - endpoint. - * `alpn` {string} An ALPN protocol identifier. - * `ca` {string|string[]|Buffer|Buffer[]} Optionally override the trusted CA - certificates. Default is to trust the well-known CAs curated by Mozilla. - Mozilla's CAs are completely replaced when CAs are explicitly specified - using this option. The value can be a string or `Buffer`, or an `Array` of - strings and/or `Buffer`s. Any string or `Buffer` can contain multiple PEM - CAs concatenated together. The peer's certificate must be chainable to a CA - trusted by the server for the connection to be authenticated. When using - certificates that are not chainable to a well-known CA, the certificate's CA - must be explicitly specified as a trusted or the connection will fail to - authenticate. - If the peer uses a certificate that doesn't match or chain to one of the - default CAs, use the `ca` option to provide a CA certificate that the peer's - certificate can match or chain to. - For self-signed certificates, the certificate is its own CA, and must be - provided. - For PEM encoded certificates, supported types are "TRUSTED CERTIFICATE", - "X509 CERTIFICATE", and "CERTIFICATE". - * `cert` {string|string[]|Buffer|Buffer[]} Cert chains in PEM format. One cert - chain should be provided per private key. Each cert chain should consist of - the PEM formatted certificate for a provided private `key`, followed by the - PEM formatted intermediate certificates (if any), in order, and not - including the root CA (the root CA must be pre-known to the peer, see `ca`). - When providing multiple cert chains, they do not have to be in the same - order as their private keys in `key`. If the intermediate certificates are - not provided, the peer will not be able to validate the certificate, and the - handshake will fail. - * `ciphers` {string} Cipher suite specification, replacing the default. For - more information, see [modifying the default cipher suite][]. Permitted - ciphers can be obtained via [`tls.getCiphers()`][]. Cipher names must be - uppercased in order for OpenSSL to accept them. - * `clientCertEngine` {string} Name of an OpenSSL engine which can provide the - client certificate. - * `crl` {string|string[]|Buffer|Buffer[]} PEM formatted CRLs (Certificate - Revocation Lists). - * `defaultEncoding` {string} The default encoding that is used when no - encoding is specified as an argument to `quicstream.write()`. Default: - `'utf8'`. - * `dhparam` {string|Buffer} Diffie Hellman parameters, required for - [Perfect Forward Secrecy][]. Use `openssl dhparam` to create the parameters. - The key length must be greater than or equal to 1024 bits, otherwise an - error will be thrown. It is strongly recommended to use 2048 bits or larger - for stronger security. If omitted or invalid, the parameters are silently - discarded and DHE ciphers will not be available. - * `ecdhCurve` {string} A string describing a named curve or a colon separated - list of curve NIDs or names, for example `P-521:P-384:P-256`, to use for - ECDH key agreement. Set to `auto` to select the - curve automatically. Use [`crypto.getCurves()`][] to obtain a list of - available curve names. On recent releases, `openssl ecparam -list_curves` - will also display the name and description of each available elliptic curve. - **Default:** [`tls.DEFAULT_ECDH_CURVE`][]. - * `highWaterMark` {number} Total number of bytes that the `QuicStream` may - buffer internally before the `quicstream.write()` function starts returning - `false`. Default: `16384`. - * `honorCipherOrder` {boolean} Attempt to use the server's cipher suite - preferences instead of the client's. When `true`, causes - `SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see - [OpenSSL Options][] for more information. - * `idleTimeout` {number} - * `key` {string|string[]|Buffer|Buffer[]|Object[]} Private keys in PEM format. - PEM allows the option of private keys being encrypted. Encrypted keys will - be decrypted with `options.passphrase`. Multiple keys using different - algorithms can be provided either as an array of unencrypted key strings or - buffers, or an array of objects in the form `{pem: <string|buffer>[, - passphrase: <string>]}`. The object form can only occur in an array. - `object.passphrase` is optional. Encrypted keys will be decrypted with - `object.passphrase` if provided, or `options.passphrase` if it is not. - * `lookup` {Function} A [custom DNS lookup function][]. - **Default**: undefined. - * `activeConnectionIdLimit` {number} Must be a value between `2` and `8` - (inclusive). Default: `2`. - * `congestionAlgorithm` {string} Must be either `'reno'` or `'cubic'`. - **Default**: `'reno'`. - * `maxAckDelay` {number} - * `maxData` {number} - * `maxUdpPayloadSize` {number} - * `maxStreamDataBidiLocal` {number} - * `maxStreamDataBidiRemote` {number} - * `maxStreamDataUni` {number} - * `maxStreamsBidi` {number} - * `maxStreamsUni` {number} - * `h3` {Object} HTTP/3 Specific Configuration Options - * `qpackMaxTableCapacity` {number} - * `qpackBlockedStreams` {number} - * `maxHeaderListSize` {number} - * `maxPushes` {number} - * `ocspHandler` {Function} A function for handling [OCSP responses][]. - * `passphrase` {string} Shared passphrase used for a single private key and/or - a PFX. - * `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded - private key and certificate chain. `pfx` is an alternative to providing - `key` and `cert` individually. PFX is usually encrypted, if it is, - `passphrase` will be used to decrypt it. Multiple PFX can be provided either - as an array of unencrypted PFX buffers, or an array of objects in the form - `{buf: <string|buffer>[, passphrase: <string>]}`. The object form can only - occur in an array. `object.passphrase` is optional. Encrypted PFX will be - decrypted with `object.passphrase` if provided, or `options.passphrase` if - it is not. - * `port` {number} The IP port of the remote QUIC server. - * `preferredAddressPolicy` {string} `'accept'` or `'reject'`. When `'accept'`, - indicates that the client will automatically use the preferred address - advertised by the server. - * `remoteTransportParams` {Buffer|TypedArray|DataView} The serialized remote - transport parameters from a previously established session. These would - have been provided as part of the `'sessionTicket'` event on a previous - `QuicClientSession` object. - * `qlog` {boolean} Whether to enable ['qlog'][] for this session. - Default: `false`. - * `secureOptions` {number} Optionally affect the OpenSSL protocol behavior, - which is not usually necessary. This should be used carefully if at all! - Value is a numeric bitmask of the `SSL_OP_*` options from - [OpenSSL Options][]. - * `servername` {string} The SNI servername. - * `sessionTicket`: {Buffer|TypedArray|DataView} The serialized TLS Session - Ticket from a previously established session. These would have been - provided as part of the `'sessionTicket`' event on a previous - `QuicClientSession` object. - * `type`: {string} Identifies the type of UDP socket. The value must either - be `'udp4'`, indicating UDP over IPv4, or `'udp6'`, indicating UDP over - IPv6. **Default**: `'udp4'`. -* Returns: {Promise} - -Returns a `Promise` that resolves a new `QuicClientSession`. - -#### `quicsocket.destroy([error])` -<!-- YAML -added: v15.0.0 ---> - -* `error` {any} - -Destroys the `QuicSocket` then emits the `'close'` event when done. The `'error'` -event will be emitted after `'close'` if the `error` is not `undefined`. - -#### `quicsocket.destroyed` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Will be `true` if the `QuicSocket` has been destroyed. - -Read-only. - -#### `quicsocket.duration` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The length of time this `QuicSocket` has been active, - -Read-only. - -#### `quicsocket.endpoints` -<!-- YAML -added: v15.0.0 ---> - -* Type: {QuicEndpoint[]} - -An array of `QuicEndpoint` instances associated with the `QuicSocket`. - -Read-only. - -#### `quicsocket.listen([options])` -<!-- YAML -added: v15.0.0 ---> - -* `options` {Object} - * `alpn` {string} A required ALPN protocol identifier. - * `ca` {string|string[]|Buffer|Buffer[]} Optionally override the trusted CA - certificates. Default is to trust the well-known CAs curated by Mozilla. - Mozilla's CAs are completely replaced when CAs are explicitly specified - using this option. The value can be a string or `Buffer`, or an `Array` of - strings and/or `Buffer`s. Any string or `Buffer` can contain multiple PEM - CAs concatenated together. The peer's certificate must be chainable to a CA - trusted by the server for the connection to be authenticated. When using - certificates that are not chainable to a well-known CA, the certificate's CA - must be explicitly specified as a trusted or the connection will fail to - authenticate. - If the peer uses a certificate that doesn't match or chain to one of the - default CAs, use the `ca` option to provide a CA certificate that the peer's - certificate can match or chain to. - For self-signed certificates, the certificate is its own CA, and must be - provided. - For PEM encoded certificates, supported types are "TRUSTED CERTIFICATE", - "X509 CERTIFICATE", and "CERTIFICATE". - * `cert` {string|string[]|Buffer|Buffer[]} Cert chains in PEM format. One cert - chain should be provided per private key. Each cert chain should consist of - the PEM formatted certificate for a provided private `key`, followed by the - PEM formatted intermediate certificates (if any), in order, and not - including the root CA (the root CA must be pre-known to the peer, see `ca`). - When providing multiple cert chains, they do not have to be in the same - order as their private keys in `key`. If the intermediate certificates are - not provided, the peer will not be able to validate the certificate, and the - handshake will fail. - * `ciphers` {string} Cipher suite specification, replacing the default. For - more information, see [modifying the default cipher suite][]. Permitted - ciphers can be obtained via [`tls.getCiphers()`][]. Cipher names must be - uppercased in order for OpenSSL to accept them. - * `clientCertEngine` {string} Name of an OpenSSL engine which can provide the - client certificate. - * `clientHelloHandler` {Function} An async function that may be used to - set a {tls.SecureContext} for the given server name at the start of the - TLS handshake. See [Handling client hello][] for details. - * `crl` {string|string[]|Buffer|Buffer[]} PEM formatted CRLs (Certificate - Revocation Lists). - * `defaultEncoding` {string} The default encoding that is used when no - encoding is specified as an argument to `quicstream.write()`. Default: - `'utf8'`. - * `dhparam` {string|Buffer} Diffie Hellman parameters, required for - [Perfect Forward Secrecy][]. Use `openssl dhparam` to create the parameters. - The key length must be greater than or equal to 1024 bits, otherwise an - error will be thrown. It is strongly recommended to use 2048 bits or larger - for stronger security. If omitted or invalid, the parameters are silently - discarded and DHE ciphers will not be available. - * `earlyData` {boolean} Set to `false` to disable 0RTT early data. - Default: `true`. - * `ecdhCurve` {string} A string describing a named curve or a colon separated - list of curve NIDs or names, for example `P-521:P-384:P-256`, to use for - ECDH key agreement. Set to `auto` to select the - curve automatically. Use [`crypto.getCurves()`][] to obtain a list of - available curve names. On recent releases, `openssl ecparam -list_curves` - will also display the name and description of each available elliptic curve. - **Default:** [`tls.DEFAULT_ECDH_CURVE`][]. - * `highWaterMark` {number} Total number of bytes that `QuicStream` instances - may buffer internally before the `quicstream.write()` function starts - returning `false`. Default: `16384`. - * `honorCipherOrder` {boolean} Attempt to use the server's cipher suite - references instead of the client's. When `true`, causes - `SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see - [OpenSSL Options][] for more information. - * `idleTimeout` {number} - * `key` {string|string[]|Buffer|Buffer[]|Object[]} Private keys in PEM format. - PEM allows the option of private keys being encrypted. Encrypted keys will - be decrypted with `options.passphrase`. Multiple keys using different - algorithms can be provided either as an array of unencrypted key strings or - buffers, or an array of objects in the form `{pem: <string|buffer>[, - passphrase: <string>]}`. The object form can only occur in an array. - `object.passphrase` is optional. Encrypted keys will be decrypted with - `object.passphrase` if provided, or `options.passphrase` if it is not. - * `lookup` {Function} A [custom DNS lookup function][]. - **Default**: undefined. - * `activeConnectionIdLimit` {number} - * `congestionAlgorithm` {string} Must be either `'reno'` or `'cubic'`. - **Default**: `'reno'`. - * `maxAckDelay` {number} - * `maxData` {number} - * `maxUdpPayloadSize` {number} - * `maxStreamsBidi` {number} - * `maxStreamsUni` {number} - * `maxStreamDataBidiLocal` {number} - * `maxStreamDataBidiRemote` {number} - * `maxStreamDataUni` {number} - * `ocspHandler` {Function} A function for handling [OCSP requests][]. - * `passphrase` {string} Shared passphrase used for a single private key - and/or a PFX. - * `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded - private key and certificate chain. `pfx` is an alternative to providing - `key` and `cert` individually. PFX is usually encrypted, if it is, - `passphrase` will be used to decrypt it. Multiple PFX can be provided either - as an array of unencrypted PFX buffers, or an array of objects in the form - `{buf: <string|buffer>[, passphrase: <string>]}`. The object form can only - occur in an array. `object.passphrase` is optional. Encrypted PFX will be - decrypted with `object.passphrase` if provided, or `options.passphrase` if - it is not. - * `preferredAddress` {Object} - * `address` {string} - * `port` {number} - * `type` {string} `'udp4'` or `'udp6'`. - * `requestCert` {boolean} Request a certificate used to authenticate the - client. - * `rejectUnauthorized` {boolean} If not `false` the server will reject any - connection which is not authorized with the list of supplied CAs. This - option only has an effect if `requestCert` is `true`. Default: `true`. - * `secureOptions` {number} Optionally affect the OpenSSL protocol behavior, - which is not usually necessary. This should be used carefully if at all! - Value is a numeric bitmask of the `SSL_OP_*` options from - [OpenSSL Options][]. - * `sessionIdContext` {string} Opaque identifier used by servers to ensure - session state is not shared between applications. Unused by clients. -* Returns: {Promise} - -Listen for new peer-initiated sessions. Returns a `Promise` that is resolved -once the `QuicSocket` is actively listening. - -#### `quicsocket.listenDuration` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The length of time this `QuicSocket` has been listening for connections. - -Read-only - -#### `quicsocket.listening` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the `QuicSocket` is listening for new connections. - -Read-only. - -#### `quicsocket.packetsIgnored` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of packets received by this `QuicSocket` that have been ignored. - -Read-only. - -#### `quicsocket.packetsReceived` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of packets successfully received by this `QuicSocket`. - -Read-only - -#### `quicsocket.packetsSent` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of packets sent by this `QuicSocket`. - -Read-only - -#### `quicsocket.pending` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Set to `true` if the socket is not yet bound to the local UDP port. - -Read-only. - -#### `quicsocket.ref()` -<!-- YAML -added: v15.0.0 ---> - -#### `quicsocket.serverBusy` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} When `true`, the `QuicSocket` will reject new connections. - -Setting `quicsocket.serverBusy` to `true` will tell the `QuicSocket` -to reject all new incoming connection requests using the `SERVER_BUSY` QUIC -error code. To begin receiving connections again, disable busy mode by setting -`quicsocket.serverBusy = false`. - -#### `quicsocket.serverBusyCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of `QuicSession` instances rejected due to server busy status. - -Read-only. - -#### `quicsocket.serverSessions` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of server `QuicSession` instances that have been associated with -this `QuicSocket`. - -Read-only. - -#### `quicsocket.setDiagnosticPacketLoss(options)` -<!-- YAML -added: v15.0.0 ---> - -* `options` {Object} - * `rx` {number} A value in the range `0.0` to `1.0` that specifies the - probability of received packet loss. - * `tx` {number} A value in the range `0.0` to `1.0` that specifies the - probability of transmitted packet loss. - -The `quicsocket.setDiagnosticPacketLoss()` method is a diagnostic only tool -that can be used to *simulate* packet loss conditions for this `QuicSocket` -by artificially dropping received or transmitted packets. - -This method is *not* to be used in production applications. - -#### `quicsocket.statelessReset` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} `true` if stateless reset processing is enabled; `false` - if disabled. - -By default, a listening `QuicSocket` will generate stateless reset tokens when -appropriate. The `disableStatelessReset` option may be set when the -`QuicSocket` is created to disable generation of stateless resets. The -`quicsocket.statelessReset` property allows stateless reset to be turned on and -off dynamically through the lifetime of the `QuicSocket`. - -#### `quicsocket.statelessResetCount` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The number of stateless resets that have been sent. - -Read-only. - -#### `quicsocket.unref();` -<!-- YAML -added: v15.0.0 ---> - -### Class: `QuicStream extends stream.Duplex` -<!-- YAML -added: v15.0.0 ---> - -* Extends: {stream.Duplex} - -#### Event: `'blocked'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when the `QuicStream` has been prevented from sending queued data for -the `QuicStream` due to congestion control. - -#### Event: `'close'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when the `QuicStream` has is completely closed and the underlying -resources have been freed. - -#### Event: `'data'` -<!-- YAML -added: v15.0.0 ---> - -#### Event: `'end'` -<!-- YAML -added: v15.0.0 ---> - -#### Event: `'error'` -<!-- YAML -added: v15.0.0 ---> - -#### Event: `'informationalHeaders'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when the `QuicStream` has received a block of informational headers. - -Support for headers depends entirely on the QUIC Application used as identified -by the `alpn` configuration option. In QUIC Applications that support headers, -informational header blocks typically come before initial headers. - -The event handler is invoked with a single argument representing the block of -Headers as an object. - -```js -stream('informationalHeaders', (headers) => { - // Use headers -}); -``` - -#### Event: `'initialHeaders'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when the `QuicStream` has received a block of initial headers. - -Support for headers depends entirely on the QUIC Application used as identified -by the `alpn` configuration option. HTTP/3, for instance, supports two kinds of -initial headers: request headers for HTTP request messages and response headers -for HTTP response messages. For HTTP/3 QUIC streams, request and response -headers are each emitted using the `'initialHeaders'` event. - -The event handler is invoked with a single argument representing the block of -Headers as an object. - -```js -stream('initialHeaders', (headers) => { - // Use headers -}); -``` - -#### Event: `'trailingHeaders'` -<!-- YAML -added: v15.0.0 ---> - -Emitted when the `QuicStream` has received a block of trailing headers. - -Support for headers depends entirely on the QUIC Application used as identified -by the `alpn` configuration option. Trailing headers typically follow any data -transmitted on the `QuicStream`, and therefore typically emit sometime after the -last `'data'` event but before the `'close'` event. The precise timing may -vary from one QUIC application to another. - -The event handler is invoked with a single argument representing the block of -Headers as an object. - -```js -stream('trailingHeaders', (headers) => { - // Use headers -}); -``` - -#### Event: `'readable'` -<!-- YAML -added: v15.0.0 ---> - -#### `quicstream.bidirectional` -<!--YAML -added: v15.0.0 ---> - -* Type: {boolean} - -When `true`, the `QuicStream` is bidirectional. Both the readable and -writable sides of the `QuicStream` `Duplex` are open. - -Read-only. - -#### `quicstream.bytesReceived` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of bytes received for this `QuicStream`. - -Read-only. - -#### `quicstream.bytesSent` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of bytes sent by this `QuicStream`. - -Read-only. - -#### `quicstream.clientInitiated` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Will be `true` if the `QuicStream` was initiated by a `QuicClientSession` -instance. - -Read-only. - -#### `quicstream.close()` -<!-- YAML -added: v15.0.0 ---> - -* Returns: {Promise} - -Closes the `QuicStream` by ending both sides of the `QuicStream` `Duplex`. -Returns a `Promise` that is resolved once the `QuicStream` has been destroyed. - -#### `quicstream.dataAckHistogram` -<!-- YAML -added: v15.0.0 ---> - -TBD - -#### `quicstream.dataRateHistogram` -<!-- YAML -added: v15.0.0 ---> - -TBD - -#### `quicstream.dataSizeHistogram` -<!-- YAML -added: v15.0.0 ---> -TBD - -#### `quicstream.duration` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The length of time the `QuicStream` has been active. - -Read-only. - -#### `quicstream.finalSize` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The total number of bytes successfully received by the `QuicStream`. - -Read-only. - -#### `quicstream.id` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The numeric identifier of the `QuicStream`. - -Read-only. - -#### `quicstream.maxAcknowledgedOffset` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The highest acknowledged data offset received for this `QuicStream`. - -Read-only. - -#### `quicstream.maxExtendedOffset` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The maximum extended data offset that has been reported to the connected peer. - -Read-only. - -#### `quicstream.maxReceivedOffset` -<!-- YAML -added: v15.0.0 ---> - -* Type: {number} - -The maximum received offset for this `QuicStream`. - -Read-only. - -#### `quicstream.pushStream(headers[, options])` -<!-- YAML -added: v15.0.0 ---> - -* `headers` {Object} An object representing a block of headers to be - transmitted with the push promise. -* `options` {Object} - * `highWaterMark` {number} Total number of bytes that the `QuicStream` may - buffer internally before the `quicstream.write()` function starts returning - `false`. Default: `16384`. - * `defaultEncoding` {string} The default encoding that is used when no - encoding is specified as an argument to `quicstream.write()`. Default: - `'utf8'`. - -* Returns: {QuicStream} - -If the selected QUIC application protocol supports push streams, then the -`pushStream()` method will initiate a new push promise and create a new -unidirectional `QuicStream` object used to fulfill that push. - -Currently only HTTP/3 supports the use of `pushStream()`. - -If the selected QUIC application protocol does not support push streams, an -error will be thrown. - -#### `quicstream.serverInitiated` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Will be `true` if the `QuicStream` was initiated by a `QuicServerSession` -instance. - -Read-only. - -#### `quicstream.session` -<!-- YAML -added: v15.0.0 ---> - -* Type: {QuicSession} - -The `QuicServerSession` or `QuicClientSession` to which the -`QuicStream` belongs. - -Read-only. - -#### `quicstream.sendFD(fd[, options])` -<!-- YAML -added: v15.0.0 ---> - -* `fd` {number|FileHandle} A readable file descriptor. -* `options` {Object} - * `offset` {number} The offset position at which to begin reading. - Default: `-1`. - * `length` {number} The amount of data from the fd to send. - Default: `-1`. - -Instead of using a `QuicStream` as a writable stream, send data from a given -file descriptor. - -If `offset` is set to a non-negative number, reading starts from that position -and the file offset will not be advanced. -If `length` is set to a non-negative number, it gives the maximum number of -bytes that are read from the file. - -The file descriptor or `FileHandle` is not closed when the stream is closed, -so it will need to be closed manually once it is no longer needed. -Using the same file descriptor concurrently for multiple streams -is not supported and may result in data loss. Re-using a file descriptor -after a stream has finished is supported. - -#### `quicstream.sendFile(path[, options])` -<!-- YAML -added: v15.0.0 ---> - -* `path` {string|Buffer|URL} -* `options` {Object} - * `onError` {Function} Callback function invoked in the case of an - error before send. - * `offset` {number} The offset position at which to begin reading. - Default: `-1`. - * `length` {number} The amount of data from the fd to send. - Default: `-1`. - -Instead of using a `QuicStream` as a writable stream, send data from a given -file path. - -The `options.onError` callback will be called if the file could not be opened. -If `offset` is set to a non-negative number, reading starts from that position. -If `length` is set to a non-negative number, it gives the maximum number of -bytes that are read from the file. - -#### `quicstream.submitInformationalHeaders(headers)` -<!-- YAML -added: v15.0.0 ---> -* `headers` {Object} - -TBD - -#### `quicstream.submitInitialHeaders(headers)` -<!-- YAML -added: v15.0.0 ---> -* `headers` {Object} - -TBD - -#### `quicstream.submitTrailingHeaders(headers)` -<!-- YAML -added: v15.0.0 ---> -* `headers` {Object} - -TBD - -#### `quicstream.unidirectional` -<!-- YAML -added: v15.0.0 ---> - -* Type: {boolean} - -Will be `true` if the `QuicStream` is unidirectional. Whether the `QuicStream` -will be readable or writable depends on whether the `quicstream.session` is -a `QuicClientSession` or `QuicServerSession`, and whether the `QuicStream` -was initiated locally or remotely. - -| `quicstream.session` | `quicstream.serverInitiated` | Readable | Writable | -| -------------------- | ---------------------------- | -------- | -------- | -| `QuicClientSession` | `true` | Y | N | -| `QuicServerSession` | `true` | N | Y | -| `QuicClientSession` | `false` | N | Y | -| `QuicServerSession` | `false` | Y | N | - -| `quicstream.session` | `quicstream.clientInitiated` | Readable | Writable | -| -------------------- | ---------------------------- | -------- | -------- | -| `QuicClientSession` | `true` | N | Y | -| `QuicServerSession` | `true` | Y | N | -| `QuicClientSession` | `false` | Y | N | -| `QuicServerSession` | `false` | N | Y | - -Read-only. - -## Additional notes - -### Custom DNS lookup functions - -By default, the QUIC implementation uses the `dns` module's -[promisified version of `lookup()`][] to resolve domains names -into IP addresses. For most typical use cases, this will be -sufficient. However, it is possible to pass a custom `lookup` -function as an option in several places throughout the QUIC API: - -* `net.createQuicSocket()` -* `quicsocket.addEndpoint()` -* `quicsocket.connect()` -* `quicsocket.listen()` - -The custom `lookup` function must return a `Promise` that is -resolved once the lookup is complete. It will be invoked with -two arguments: - -* `address` {string|undefined} The host name to resolve, or - `undefined` if no host name was provided. -* `family` {number} One of `4` or `6`, identifying either - IPv4 or IPv6. - -```js -async function myCustomLookup(address, type) { - // TODO(@jasnell): Make this example more useful - return resolveTheAddressSomehow(address, type); -} -``` - -### Online Certificate Status Protocol (OCSP) - -The QUIC implementation supports use of OCSP during the TLS 1.3 handshake -of a new QUIC session. - -#### Requests - -A `QuicServerSession` can receive and process OCSP requests by setting the -`ocspHandler` option in the `quicsocket.listen()` function. The value of -the `ocspHandler` is an async function that must return an object with the -OCSP response and, optionally, a new {tls.SecureContext} to use during the -handshake. - -The handler function will be invoked with two arguments: - -* `type`: {string} Will always be `request` for `QuicServerSession`. -* `options`: {Object} - * `servername` {string} The SNI server name. - * `context` {tls.SecureContext} The `SecureContext` currently used. - -```js -async function ocspServerHandler(type, { servername, context }) { - // Process the request... - return { data: Buffer.from('The OCSP response') }; -} - -sock.listen({ ocspHandler: ocspServerHandler }); -``` - -#### Responses - -A `QuicClientSession` can receive and process OCSP responses by setting the -`ocspHandler` option in the `quicsocket.connect()` function. The value of -the `ocspHandler` is an async function with no expected return value. - -The handler function will be invoked with two arguments: - -* `type`: {string} Will always be `response` for `QuicClientSession`. -* `options`: {Object} - * `data`: {Buffer} The OCSP response provided by the server - -```js -async function ocspClientHandler(type, { data }) { - console.log(data.toString()); -} - -sock.connect({ ocspHandler: ocspClientHandler }); -``` - -### Handling client hello - -When `quicsocket.listen()` is called, a {tls.SecureContext} is created and used -by default for all new `QuicServerSession` instances. There are times, however, -when the {tls.SecureContext} to be used for a `QuicSession` can only be -determined once the client initiates a connection. This is accomplished using -the `clientHelloHandler` option when calling `quicsocket.listen()`. - -The value of `clientHelloHandler` is an async function that is called at the -start of a new `QuicServerSession`. It is invoked with three arguments: - -* `alpn` {string} The ALPN protocol identifier specified by the client. -* `servername` {string} The SNI server name specified by the client. -* `ciphers` {string[]} The array of TLS 1.3 ciphers specified by the client. - -The `clientHelloHandler` can return a new {tls.SecureContext} object that will -be used to continue the TLS handshake. If the function returns `undefined`, the -default {tls.SecureContext} will be used. Returning any other value will cause -an error to be thrown that will destroy the `QuicServerSession` instance. - -```js -const server = createQuicSocket(); - -server.listen({ - async clientHelloHandler(alpn, servername, ciphers) { - console.log(alpn); - console.log(servername); - console.log(ciphers); - } -}); -``` - -[ALPN]: https://tools.ietf.org/html/rfc7301 -[Certificate Object]: https://nodejs.org/dist/latest-v12.x/docs/api/tls.html#tls_certificate_object -[Handling client hello]: #quic_handling_client_hello -[OCSP requests]: #quic_online_certificate_status_protocol_ocsp -[OCSP responses]: #quic_online_certificate_status_protocol_ocsp -[OpenSSL Options]: crypto.md#crypto_openssl_options -[Perfect Forward Secrecy]: #tls_perfect_forward_secrecy -[RFC 4007]: https://tools.ietf.org/html/rfc4007 -[`crypto.getCurves()`]: crypto.md#crypto_crypto_getcurves -[`stream.Readable`]: #stream_class_stream_readable -[`tls.DEFAULT_ECDH_CURVE`]: #tls_tls_default_ecdh_curve -[`tls.getCiphers()`]: tls.md#tls_tls_getciphers -[custom DNS lookup function]: #quic_custom_dns_lookup_functions -[modifying the default cipher suite]: tls.md#tls_modifying_the_default_tls_cipher_suite -[promisified version of `lookup()`]: dns.md#dns_dnspromises_lookup_hostname_options -['qlog']: #quic_quicsession_qlog -[qlog standard]: https://tools.ietf.org/id/draft-marx-qlog-event-definitions-quic-h3-00.html diff --git a/doc/guides/maintaining-ngtcp2-nghttp3.md b/doc/guides/maintaining-ngtcp2-nghttp3.md deleted file mode 100644 index 1fa065f6df1..00000000000 --- a/doc/guides/maintaining-ngtcp2-nghttp3.md +++ /dev/null @@ -1,59 +0,0 @@ -# Maintaining ngtcp2 and nghttp3 - -The ngtcp2 and nghttp3 libraries form the foundation of the QUIC -implementation. They are acquired from the [ngtcp2/ngtcp2][] and -[ngtcp2/nghttp3][] repositories on GitHub. - -ngtcp2 and nghttp3 are tightly related. They will typically be -updated together. - -## Updating ngtcp2 - -Update ngtcp2: -```bash -git clone https://github.com/ngtcp2/ngtcp2 -cd ngtcp2 -autoreconf -i -./configure --enable-lib-only -cd .. -cp -R ngtcp2/crypto node/deps/ngtcp2 -cp -R ngtcp2/lib node/deps/ngtcp2 -``` - -The `autoreconf -i` and `./configure --enable-lib-only` commands -ensure that the necessary template files (such as version.h.in -located in lib/includes/ngtcp2/ are processed appropriately. - -Check that Node.js still builds and tests. - -## Updating nghttp3 - -Update nghttp3: -```bash -git clone https://github.com/ngtcp2/nghttp3 -cd nghttp3 -autoreconf -i -./configure --enable-lib-only -cd .. -cp -R nghttp3/lib node/deps/nghttp3 -``` - -The `autoreconf -i` and `./configure --enable-lib-only` commands -ensure that the necessary template files (such as version.h.in -located in lib/includes/ngtcp2/ are processed appropriately. - -Check that Node.js still builds and tests. - -## Commiting ngtcp2 and nghttp3 - -Use: `git add --all deps/ngtcp2` and `git add --all deps/nghttp3` - -Commit the changes with a message like -```text -deps: update ngtcp2 and nghttp3 - -Updated as described in doc/guides/maintaining-ngtcp2-nghttp3.md. -``` - -[ngtcp2/nghttp3]: https://github.com/ngtcp2/nghttp3 -[ngtcp2/ngtcp2]: https://github.com/ngtcp2/ngtcp2 diff --git a/doc/guides/maintaining-openssl.md b/doc/guides/maintaining-openssl.md index d4ac9ad4293..0d5ed344904 100644 --- a/doc/guides/maintaining-openssl.md +++ b/doc/guides/maintaining-openssl.md @@ -6,11 +6,10 @@ If you need to provide updates across all active release lines you will currently need to generate three PRs as follows: * a PR for master which is generated following the instructions - below which include the QUIC patch. + below. * a PR for 14.x following the instruction below based on the - 14,x branch but skipping the step to apply the QUICK patch. - This PR should cherry pick back to the active release lines - except for the 10.x line. + 14,x branch. This PR should cherry pick back to the active release + lines except for the 10.x line. * a PR which uses the same commit from the second PR to apply the updates to the openssl source code, with a new commit generated by following steps 2 onwards on the 10.x line. This is @@ -68,28 +67,6 @@ This updates all sources in deps/openssl/openssl by: $ git commit openssl ``` -#### Updating the QUIC APIs - -The APIs to support the QUIC implementation are a port of the BoringSSL -implementation that has not yet landed in OpenSSL. They must be re-applied -separately after updating the openssl source as described above. The -current patch implementation can be found in the `deps/openssl/patches` -directory in the file `0001-deps-add-support-for-BoringSSL-QUIC-APIs.patch`. - -```console -% git am deps/openssl/patches 0001-deps-add-support-for-BoringSSL-QUIC-APIs.patch -``` - -The patch file itself is generated by squashing commits from the -`OpenSSL_1_1_1d-quic` fork of the Akamai OpenSSL fork located -[here](https://github.com/akamai/openssl), starting with -[this commit](https://github.com/akamai/openssl/commit/f910151a5b60eb7b90d274332368226cc67479df), -then applying additional edits to update the implementation to -openssl-1.1.1e. As OpenSSL updates are made, additional updates -to the patch may be necessary to keep the patch in sync. - -The patch is currently supported only for openssl-1.1.1e. - ## 2. Execute `make` in `deps/openssl/config` directory Use `make` to regenerate all platform dependent files in diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 35b091c1853..631f3129135 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1341,19 +1341,6 @@ E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath, base = undefined) => { return `Package subpath '${subpath}' is not defined by "exports" in ${ pkgPath}package.json${base ? ` imported from ${base}` : ''}`; }, Error); -E('ERR_QUIC_FAILED_TO_CREATE_SESSION', 'Failed to create QuicSession', Error); -E('ERR_QUIC_INVALID_REMOTE_TRANSPORT_PARAMS', - 'Invalid remote transport params', Error); -E('ERR_QUIC_INVALID_TLS_SESSION_TICKET', - 'Invalid TLS session ticket', Error); -E('ERR_QUIC_VERSION_NEGOTIATION', - (version, requestedVersions, supportedVersions) => { - const requestedVersionsString = ArrayPrototypeJoin(requestedVersions, ', '); - return 'QUIC session received version negotiation from server. ' + - `Version: ${version}. Requested: ${requestedVersionsString} ` + - `Supported: ${ArrayPrototypeJoin(supportedVersions, ', ')}`; - }, - Error); E('ERR_REQUIRE_ESM', (filename, parentPath = null, packageJsonPath = null) => { let msg = `Must use import to load ES Module: ${filename}`; diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js deleted file mode 100644 index 0c326872a7a..00000000000 --- a/lib/internal/quic/core.js +++ /dev/null @@ -1,3326 +0,0 @@ -'use strict'; - -/* eslint-disable no-use-before-define */ - -const { - assertCrypto, - customInspectSymbol: kInspect, -} = require('internal/util'); - -assertCrypto(); - -const { - ArrayFrom, - ArrayPrototypePush, - BigInt64Array, - Boolean, - Error, - FunctionPrototypeBind, - FunctionPrototypeCall, - Map, - Number, - Promise, - PromiseAll, - PromisePrototypeThen, - PromisePrototypeCatch, - PromisePrototypeFinally, - PromiseReject, - PromiseResolve, - ReflectApply, - SafeSet, - Symbol, - SymbolFor, -} = primordials; - -const { Buffer } = require('buffer'); -const { isArrayBufferView } = require('internal/util/types'); -const { - customInspect, - getAllowUnauthorized, - getSocketType, - setTransportParams, - toggleListeners, - validateNumber, - validateTransportParams, - validateQuicClientSessionOptions, - validateQuicSocketOptions, - validateQuicStreamOptions, - validateQuicSocketListenOptions, - validateQuicEndpointOptions, - validateCreateSecureContextOptions, - validateQuicSocketConnectOptions, - QuicStreamSharedState, - QuicSocketSharedState, - QuicSessionSharedState, - QLogStream, -} = require('internal/quic/util'); -const assert = require('internal/assert'); -const { EventEmitter, once } = require('events'); -const fs = require('fs'); -const fsPromisesInternal = require('internal/fs/promises'); -const { Duplex } = require('stream'); -const { - createSecureContext: _createSecureContext -} = require('tls'); -const BlockList = require('internal/blocklist'); -const { - translatePeerCertificate -} = require('_tls_common'); -const { - defaultTriggerAsyncIdScope, - symbols: { - async_id_symbol, - owner_symbol, - }, -} = require('internal/async_hooks'); -const dgram = require('dgram'); -const internalDgram = require('internal/dgram'); -const { - assertValidPseudoHeader, - assertValidPseudoHeaderResponse, - assertValidPseudoHeaderTrailer, - mapToHeaders, -} = require('internal/http2/util'); - -const { - constants: { - UV_UDP_IPV6ONLY, - UV_UDP_REUSEADDR, - } -} = internalBinding('udp_wrap'); - -const { - writeGeneric, - writevGeneric, - onStreamRead, - kAfterAsyncWrite, - kMaybeDestroy, - kUpdateTimer, - kHandle, - setStreamTimeout // eslint-disable-line no-unused-vars -} = require('internal/stream_base_commons'); - -const { - ShutdownWrap, - kReadBytesOrError, - streamBaseState -} = internalBinding('stream_wrap'); - -const { - codes: { - ERR_INVALID_ARG_TYPE, - ERR_INVALID_STATE, - ERR_OPERATION_FAILED, - ERR_QUIC_FAILED_TO_CREATE_SESSION, - ERR_QUIC_INVALID_REMOTE_TRANSPORT_PARAMS, - ERR_QUIC_INVALID_TLS_SESSION_TICKET, - ERR_QUIC_VERSION_NEGOTIATION, - ERR_TLS_DH_PARAM_SIZE, - }, - hideStackFrames, - errnoException, - exceptionWithHostPort -} = require('internal/errors'); - -const { FileHandle } = internalBinding('fs'); -const { StreamPipe } = internalBinding('stream_pipe'); -const { UV_EOF } = internalBinding('uv'); - -const { - QuicSocket: QuicSocketHandle, - QuicEndpoint: QuicEndpointHandle, - initSecureContext, - initSecureContextClient, - createClientSession: _createClientSession, - openBidirectionalStream: _openBidirectionalStream, - openUnidirectionalStream: _openUnidirectionalStream, - setCallbacks, - constants: { - AF_INET6, - NGTCP2_DEFAULT_MAX_PKTLEN, - IDX_QUIC_SESSION_STATS_CREATED_AT, - IDX_QUIC_SESSION_STATS_DESTROYED_AT, - IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT, - IDX_QUIC_SESSION_STATS_BYTES_RECEIVED, - IDX_QUIC_SESSION_STATS_BYTES_SENT, - IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT, - IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT, - IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT, - IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT, - IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT, - IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT, - IDX_QUIC_SESSION_STATS_HANDSHAKE_COMPLETED_AT, - IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT, - IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT, - IDX_QUIC_SESSION_STATS_BLOCK_COUNT, - IDX_QUIC_SESSION_STATS_MIN_RTT, - IDX_QUIC_SESSION_STATS_SMOOTHED_RTT, - IDX_QUIC_SESSION_STATS_LATEST_RTT, - IDX_QUIC_STREAM_STATS_CREATED_AT, - IDX_QUIC_STREAM_STATS_DESTROYED_AT, - IDX_QUIC_STREAM_STATS_BYTES_RECEIVED, - IDX_QUIC_STREAM_STATS_BYTES_SENT, - IDX_QUIC_STREAM_STATS_MAX_OFFSET, - IDX_QUIC_STREAM_STATS_FINAL_SIZE, - IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK, - IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV, - IDX_QUIC_SOCKET_STATS_CREATED_AT, - IDX_QUIC_SOCKET_STATS_DESTROYED_AT, - IDX_QUIC_SOCKET_STATS_BOUND_AT, - IDX_QUIC_SOCKET_STATS_LISTEN_AT, - IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED, - IDX_QUIC_SOCKET_STATS_BYTES_SENT, - IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED, - IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED, - IDX_QUIC_SOCKET_STATS_PACKETS_SENT, - IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS, - IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS, - IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT, - IDX_QUIC_SOCKET_STATS_SERVER_BUSY_COUNT, - ERR_FAILED_TO_CREATE_SESSION, - ERR_INVALID_REMOTE_TRANSPORT_PARAMS, - ERR_INVALID_TLS_SESSION_TICKET, - NGTCP2_PATH_VALIDATION_RESULT_FAILURE, - NGTCP2_NO_ERROR, - QUIC_ERROR_APPLICATION, - QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED, - QUICSERVERSESSION_OPTION_REQUEST_CERT, - QUICCLIENTSESSION_OPTION_REQUEST_OCSP, - QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY, - QUICSOCKET_OPTIONS_VALIDATE_ADDRESS, - QUICSTREAM_HEADERS_KIND_NONE, - QUICSTREAM_HEADERS_KIND_INFORMATIONAL, - QUICSTREAM_HEADERS_KIND_INITIAL, - QUICSTREAM_HEADERS_KIND_TRAILING, - QUICSTREAM_HEADERS_KIND_PUSH, - QUICSTREAM_HEADER_FLAGS_NONE, - QUICSTREAM_HEADER_FLAGS_TERMINAL, - } -} = internalBinding('quic'); - -const { - Histogram, - kDestroy: kDestroyHistogram -} = require('internal/histogram'); - -const { - validateAbortSignal, - validateBoolean, - validateInteger, - validateObject, -} = require('internal/validators'); - -const emit = EventEmitter.prototype.emit; - -const kAddSession = Symbol('kAddSession'); -const kAddStream = Symbol('kAddStream'); -const kBind = Symbol('kBind'); -const kClose = Symbol('kClose'); -const kClientHello = Symbol('kClientHello'); -const kDestroy = Symbol('kDestroy'); -const kEndpointBound = Symbol('kEndpointBound'); -const kEndpointClose = Symbol('kEndpointClose'); -const kHandleOcsp = Symbol('kHandleOcsp'); -const kHandshake = Symbol('kHandshake'); -const kHandshakeComplete = Symbol('kHandshakeComplete'); -const kHandshakePost = Symbol('kHandshakePost'); -const kHeaders = Symbol('kHeaders'); -const kInternalState = Symbol('kInternalState'); -const kInternalClientState = Symbol('kInternalClientState'); -const kInternalServerState = Symbol('kInternalServerState'); -const kListen = Symbol('kListen'); -const kMaybeBind = Symbol('kMaybeBind'); -const kOnFileOpened = Symbol('kOnFileOpened'); -const kOnFileUnpipe = Symbol('kOnFileUnpipe'); -const kOnPipedFileHandleRead = Symbol('kOnPipedFileHandleRead'); -const kReady = Symbol('kReady'); -const kRemoveFromSocket = Symbol('kRemoveFromSocket'); -const kRemoveSession = Symbol('kRemove'); -const kRemoveStream = Symbol('kRemoveStream'); -const kServerBusy = Symbol('kServerBusy'); -const kSetHandle = Symbol('kSetHandle'); -const kSetQLogStream = Symbol('kSetQLogStream'); -const kSetSocket = Symbol('kSetSocket'); -const kStartFilePipe = Symbol('kStartFilePipe'); -const kStreamClose = Symbol('kStreamClose'); -const kStreamOptions = Symbol('kStreamOptions'); -const kStreamReset = Symbol('kStreamReset'); -const kTrackWriteState = Symbol('kTrackWriteState'); -const kUDPHandleForTesting = Symbol('kUDPHandleForTesting'); -const kUsePreferredAddress = Symbol('kUsePreferredAddress'); -const kVersionNegotiation = Symbol('kVersionNegotiation'); -const kWriteGeneric = Symbol('kWriteGeneric'); - -const kRejections = SymbolFor('nodejs.rejection'); - -const kSocketUnbound = 0; -const kSocketPending = 1; -const kSocketBound = 2; -const kSocketDestroyed = 3; - -let diagnosticPacketLossWarned = false; -let warnedVerifyHostnameIdentity = false; - -let DOMException; - -const lazyDOMException = hideStackFrames((message, name) => { - if (DOMException === undefined) - DOMException = internalBinding('messaging').DOMException; - return new DOMException(message, name); -}); - -assert(process.versions.ngtcp2 !== undefined); - -// Called by the C++ internals when the QuicSocket is closed with -// or without an error. The only thing left to do is destroy the -// QuicSocket instance. -function onSocketClose(err) { - this[owner_symbol].destroy(err != null ? errnoException(err) : undefined); -} - -// Called by the C++ internals when the server busy state of -// the QuicSocket has been changed. -function onSocketServerBusy() { - this[owner_symbol][kServerBusy](); -} - -// Called by the C++ internals when a new server QuicSession has been created. -function onSessionReady(handle) { - const socket = this[owner_symbol]; - const session = - new QuicServerSession( - socket, - handle, - socket[kStreamOptions]); - try { - socket.emit('session', session); - } catch (error) { - socket[kRejections](error, 'session', session); - } -} - -// Called when the C++ QuicSession::Close() method has been called. -// Synchronously cleanup and destroy the JavaScript QuicSession. -function onSessionClose(code, family, silent, statelessReset) { - this[owner_symbol][kDestroy](code, family, silent, statelessReset); -} - -// This callback is invoked at the start of the TLS handshake to provide -// some basic information about the ALPN, SNI, and Ciphers that are -// being requested. It is only called if the 'clientHelloHandler' option is -// specified on listen(). -function onSessionClientHello(alpn, servername, ciphers) { - PromisePrototypeThen( - this[owner_symbol][kClientHello](alpn, servername, ciphers), - (context) => { - if (context !== undefined && !context?.context) - throw new ERR_INVALID_ARG_TYPE('context', 'SecureContext', context); - this.onClientHelloDone(context?.context); - }, - (error) => this[owner_symbol].destroy(error) - ); -} - -// This callback is only ever invoked for QuicServerSession instances, -// and is used to trigger OCSP request processing when needed. The -// user callback must invoke .onCertDone() in order for the -// TLS handshake to continue. -function onSessionCert(servername) { - PromisePrototypeThen( - this[owner_symbol][kHandleOcsp](servername), - (data) => { - if (data !== undefined) { - if (typeof data === 'string') - data = Buffer.from(data); - if (!isArrayBufferView(data)) { - throw new ERR_INVALID_ARG_TYPE( - 'data', - ['string', 'Buffer', 'TypedArray', 'DataView'], - data); - } - } - this.onCertDone(data); - }, - (error) => this[owner_symbol].destroy(error) - ); -} - -// This callback is only ever invoked for QuicClientSession instances, -// and is used to deliver the OCSP response as provided by the server. -// If the requestOCSP configuration option is false, this will never -// be called. -function onSessionStatus(data) { - PromisePrototypeCatch( - this[owner_symbol][kHandleOcsp](data), - (error) => this[owner_symbol].destroy(error) - ); -} - -// Called by the C++ internals when the TLS handshake is completed. -function onSessionHandshake( - servername, - alpn, - cipher, - cipherVersion, - maxPacketLength, - verifyErrorReason, - verifyErrorCode, - earlyData) { - this[owner_symbol][kHandshake]( - servername, - alpn, - cipher, - cipherVersion, - maxPacketLength, - verifyErrorReason, - verifyErrorCode, - earlyData); -} - -// Called by the C++ internals when TLS session ticket data is -// available. This is generally most useful on the client side -// where the session ticket needs to be persisted for session -// resumption and 0RTT. -function onSessionTicket(sessionTicket, transportParams) { - if (this[owner_symbol]) { - process.nextTick(FunctionPrototypeBind( - emit, - this[owner_symbol], - 'sessionTicket', - sessionTicket, - transportParams - )); - } -} - -// Called by the C++ internals when path validation is completed. -// This is a purely informational event that is emitted only when -// there is a listener present for the pathValidation event. -function onSessionPathValidation(res, local, remote) { - const session = this[owner_symbol]; - if (session) { - process.nextTick(FunctionPrototypeBind( - emit, - session, - 'pathValidation', - res === NGTCP2_PATH_VALIDATION_RESULT_FAILURE ? 'failure' : 'success', - local, - remote - )); - } -} - -function onSessionUsePreferredAddress(address, port, family) { - const session = this[owner_symbol]; - session[kUsePreferredAddress]({ - address, - port, - type: family === AF_INET6 ? 'udp6' : 'udp4' - }); -} - -// Called by the C++ internals to emit a QLog record. This can -// be called before the QuicSession has been fully initialized, -// in which case we store a reference and defer emitting the -// qlog event until after we're initialized. -function onSessionQlog(handle) { - const session = this[owner_symbol]; - const stream = new QLogStream(handle); - if (session) - session[kSetQLogStream](stream); - else - this.qlogStream = stream; -} - -// Called by the C++ internals when a client QuicSession receives -// a version negotiation response from the server. -function onSessionVersionNegotiation( - version, - requestedVersions, - supportedVersions) { - if (this[owner_symbol]) { - this[owner_symbol][kVersionNegotiation]( - version, - requestedVersions, - supportedVersions); - } -} - -// Called by the C++ internals to emit keylogging details for a -// QuicSession. -function onSessionKeylog(line) { - if (this[owner_symbol]) { - this[owner_symbol].emit('keylog', line); - } -} - -// Called by the C++ internals when a new QuicStream has been created. -function onStreamReady(streamHandle, id, push_id) { - const session = this[owner_symbol]; - - // onStreamReady should never be called if the stream is in a closing - // state because new streams should not have been accepted at the C++ - // level. - assert(!session.closing); - const stream = new QuicStream({ - ...session[kStreamOptions], - writable: !(id & 0b10), - }, session, streamHandle, push_id); - process.nextTick(() => { - try { - session.emit('stream', stream); - } catch (error) { - stream.destroy(error); - } - }); -} - -// Called by the C++ internals when a stream is closed and -// needs to be destroyed on the JavaScript side. -function onStreamClose(id, appErrorCode) { - this[owner_symbol][kStreamClose](id, appErrorCode); -} - -// Called by the C++ internals when a stream has been reset -function onStreamReset(id, appErrorCode) { - this[owner_symbol][kStreamReset](id, appErrorCode); -} - -// Called when an error occurs in a QuicStream -function onStreamError(streamHandle, error) { - streamHandle[owner_symbol].destroy(error); -} - -// Called when a block of headers has been fully -// received for the stream. Not all QuicStreams -// will support headers. The headers argument -// here is an Array of name-value pairs. -function onStreamHeaders(id, headers, kind, push_id) { - this[owner_symbol][kHeaders](id, headers, kind, push_id); -} - -// When a stream is flow control blocked, causes a blocked event -// to be emitted. This is a purely informational event. -function onStreamBlocked() { - process.nextTick(FunctionPrototypeBind(emit, this[owner_symbol], 'blocked')); -} - -// Register the callbacks with the QUIC internal binding. -setCallbacks({ - onSocketClose, - onSocketServerBusy, - onSessionReady, - onSessionCert, - onSessionStatus, - onSessionClientHello, - onSessionClose, - onSessionHandshake, - onSessionKeylog, - onSessionQlog, - onSessionTicket, - onSessionVersionNegotiation, - onStreamReady, - onStreamClose, - onStreamError, - onStreamReset, - onSessionPathValidation, - onSessionUsePreferredAddress, - onStreamHeaders, - onStreamBlocked, -}); - -// Creates the SecureContext used by QuicSocket instances that are listening -// for new connections. -function createSecureContext(options, init_cb) { - const sc_options = validateCreateSecureContextOptions(options); - const { groups, earlyData } = sc_options; - const sc = _createSecureContext(sc_options); - init_cb(sc.context, groups, earlyData); - return sc; -} - -function onNewListener(event) { - toggleListeners(this[kInternalState].state, event, true); -} - -function onRemoveListener(event) { - toggleListeners(this[kInternalState].state, event, false); -} - -function getStats(obj, idx) { - const stats = obj[kHandle]?.stats || obj[kInternalState].stats; - // If stats is undefined at this point, it's just a bug - assert(stats); - return stats[idx]; -} - -function addressOrLocalhost(address, type) { - return address || (type === AF_INET6 ? '::' : '0.0.0.0'); -} - -function deferredClosePromise(state) { - return state.closePromise = PromisePrototypeFinally( - new Promise((resolve, reject) => { - state.closePromiseResolve = resolve; - state.closePromiseReject = reject; - }), - () => { - state.closePromise = undefined; - state.closePromiseResolve = undefined; - state.closePromiseReject = undefined; - } - ); -} - -async function resolvePreferredAddress(lookup, preferredAddress) { - if (preferredAddress === undefined) - return {}; - const { - address, - port, - type = 'udp4' - } = { ...preferredAddress }; - const [typeVal] = getSocketType(type); - const { - address: ip - } = await lookup(address, typeVal === AF_INET6 ? 6 : 4); - return { ip, port, type }; -} - -// QuicEndpoint wraps a UDP socket and is owned -// by a QuicSocket. It does not exist independently -// of the QuicSocket. -class QuicEndpoint { - [kInternalState] = { - state: kSocketUnbound, - bindPromise: undefined, - closePromise: undefined, - closePromiseResolve: undefined, - closePromiseReject: undefined, - socket: undefined, - udpSocket: undefined, - address: undefined, - ipv6Only: undefined, - lookup: undefined, - port: undefined, - reuseAddr: undefined, - type: undefined, - fd: undefined - }; - - constructor(socket, options) { - const { - address, - ipv6Only, - lookup, - port = 0, - reuseAddr, - type, - preferred, - } = validateQuicEndpointOptions(options); - const state = this[kInternalState]; - state.socket = socket; - state.address = addressOrLocalhost(address, type); - state.lookup = lookup; - state.ipv6Only = ipv6Only; - state.port = port; - state.reuseAddr = reuseAddr; - state.type = type; - state.udpSocket = dgram.createSocket(type === AF_INET6 ? 'udp6' : 'udp4'); - - // kUDPHandleForTesting is only used in the Node.js test suite to - // artificially test the endpoint. This code path should never be - // used in user code. - if (typeof options[kUDPHandleForTesting] === 'object') { - state.udpSocket.bind(options[kUDPHandleForTesting]); - state.state = kSocketBound; - state.socket[kEndpointBound](this); - } - const udpHandle = state.udpSocket[internalDgram.kStateSymbol].handle; - const handle = new QuicEndpointHandle(socket[kHandle], udpHandle); - handle[owner_symbol] = this; - this[kHandle] = handle; - socket[kHandle].addEndpoint(handle, preferred); - } - - [kInspect](depth, options) { - return customInspect(this, { - address: this.address, - fd: this.fd, - type: this[kInternalState].type === AF_INET6 ? 'udp6' : 'udp4', - destroyed: this.destroyed, - bound: this.bound, - pending: this.pending, - }, depth, options); - } - - bind(options) { - const state = this[kInternalState]; - if (state.bindPromise !== undefined) - return state.bindPromise; - - return state.bindPromise = PromisePrototypeFinally(this[kBind](), () => { - state.bindPromise = undefined; - }); - } - - // Binds the QuicEndpoint to the local port. Returns a Promise - // that is resolved once the QuicEndpoint binds, or rejects if - // binding was not successful. Calling bind() multiple times - // before the Promise is resolved will return the same Promise. - // Calling bind() after the endpoint is already bound will - // immediately return a resolved promise. Calling bind() after - // the endpoint has been destroyed will cause the Promise to - // be rejected. - async [kBind](options) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - - if (state.state !== kSocketUnbound) - return this.address; - - const { signal } = { ...options }; - validateAbortSignal(signal, 'options.signal'); - - // If an AbortSignal was passed in, check to make sure it is not already - // aborted before we continue on to do any work. - if (signal && signal.aborted) - throw new lazyDOMException('The operation was aborted', 'AbortError'); - - state.state = kSocketPending; - - const { - address: ip - } = await state.lookup(state.address, state.type === AF_INET6 ? 6 : 4); - - // It's possible for the QuicEndpoint to have been destroyed while - // we were waiting for the DNS lookup to complete. If so, reject - // the Promise. - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint was destroyed'); - - // If an AbortSignal was passed in, check to see if it was triggered - // while we were waiting. - if (signal && signal.aborted) { - state.state = kSocketUnbound; - throw new lazyDOMException('The operation was aborted', 'AbortError'); - } - - // From here on, any errors are fatal for the QuicEndpoint. Keep in - // mind that this means that the Bind Promise will be rejected *and* - // the QuicEndpoint will be destroyed with an error. - try { - const udpHandle = state.udpSocket[internalDgram.kStateSymbol].handle; - if (udpHandle == null) { - // It's not clear what cases trigger this but it is possible. - throw new ERR_OPERATION_FAILED('Acquiring UDP socket handle failed'); - } - - const flags = - (state.reuseAddr ? UV_UDP_REUSEADDR : 0) | - (state.ipv6Only ? UV_UDP_IPV6ONLY : 0); - - const ret = udpHandle.bind(ip, state.port, flags); - if (ret) - throw exceptionWithHostPort(ret, 'bind', ip, state.port); - - // On Windows, the fd will be meaningless, but we always record it. - state.fd = udpHandle.fd; - state.state = kSocketBound; - - return this.address; - } catch (error) { - this.destroy(error); - throw error; - } - } - - destroy(error) { - if (this.destroyed) - return; - - const state = this[kInternalState]; - state.state = kSocketDestroyed; - - const handle = this[kHandle]; - if (handle === undefined) - return; - - this[kHandle] = undefined; - handle[owner_symbol] = undefined; - // Calling waitForPendingCallbacks starts the process of - // closing down the QuicEndpoint. Once all pending writes - // to the underlying libuv udp handle have completed, the - // ondone callback will be invoked, triggering the UDP - // socket to be closed. Once it is closed, we notify - // the QuicSocket that this QuicEndpoint has been closed, - // allowing it to be freed. - handle.ondone = () => { - state.udpSocket.close((err) => { - if (err) error = err; - if (error && typeof state.closePromiseReject === 'function') - state.closePromiseReject(error); - else if (typeof state.closePromiseResolve === 'function') - state.closePromiseResolve(); - state.socket[kEndpointClose](this, error); - }); - }; - handle.waitForPendingCallbacks(); - } - - // Closes the QuicEndpoint. Returns a Promise that is resolved - // once the QuicEndpoint closes, or rejects if it closes with - // an error. Calling close() multiple times before the Promise - // is resolved will return the same Promise. Calling close() - // after will return a rejected Promise. - close() { - return this[kInternalState].closePromise || this[kClose](); - } - - [kClose]() { - if (this.destroyed) { - return PromiseReject( - new ERR_INVALID_STATE('QuicEndpoint is already destroyed')); - } - const promise = deferredClosePromise(this[kInternalState]); - this.destroy(); - return promise; - } - - // If the QuicEndpoint is bound, returns an object detailing - // the local IP address, port, and address type to which it - // is bound. Otherwise, returns an empty object. - get address() { - const state = this[kInternalState]; - if (state.state !== kSocketDestroyed) { - try { - return state.udpSocket.address(); - } catch (err) { - if (err.code === 'EBADF') { - // If there is an EBADF error, the socket is not bound. - // Return empty object. Else, rethrow the error because - // something else bad happened. - return {}; - } - throw err; - } - } - return {}; - } - - // On Windows, this always returns undefined. - get fd() { - return this[kInternalState].fd >= 0 ? - this[kInternalState].fd : undefined; - } - - // True if the QuicEndpoint has been destroyed and is - // no longer usable. - get destroyed() { - return this[kInternalState].state === kSocketDestroyed; - } - - // True if binding has been initiated and is in progress. - get pending() { - return this[kInternalState].state === kSocketPending; - } - - // True if the QuicEndpoint has been bound to the localUDP port. - get bound() { - return this[kInternalState].state === kSocketBound; - } - - setTTL(ttl) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.setTTL(ttl); - return this; - } - - setMulticastTTL(ttl) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.setMulticastTTL(ttl); - return this; - } - - setBroadcast(on = true) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.setBroadcast(on); - return this; - } - - setMulticastLoopback(on = true) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.setMulticastLoopback(on); - return this; - } - - setMulticastInterface(iface) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.setMulticastInterface(iface); - return this; - } - - addMembership(address, iface) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.addMembership(address, iface); - return this; - } - - dropMembership(address, iface) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.dropMembership(address, iface); - return this; - } - - ref() { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.ref(); - return this; - } - - unref() { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicEndpoint is already destroyed'); - state.udpSocket.unref(); - return this; - } -} - -// QuicSocket wraps a UDP socket plus the associated TLS context and QUIC -// Protocol state. There may be *multiple* QUIC connections (QuicSession) -// associated with a single QuicSocket. -class QuicSocket extends EventEmitter { - [kInternalState] = { - alpn: undefined, - bindPromise: undefined, - blockList: undefined, - client: undefined, - closePromise: undefined, - closePromiseResolve: undefined, - closePromiseReject: undefined, - defaultEncoding: undefined, - endpoints: new SafeSet(), - highWaterMark: undefined, - listenPending: false, - listenPromise: undefined, - lookup: undefined, - ocspHandler: undefined, - clientHelloHandler: undefined, - server: undefined, - serverSecureContext: undefined, - sessions: new SafeSet(), - state: kSocketUnbound, - sharedState: undefined, - stats: undefined, - }; - - constructor(options) { - const { - endpoint, - - // Default configuration for QuicClientSessions - client, - - // The maximum number of connections - maxConnections, - - // The maximum number of connections per host - maxConnectionsPerHost, - - // The maximum number of stateless resets per host - maxStatelessResetsPerHost, - - // The maximum number of seconds for retry token - retryTokenTimeout, - - // The DNS lookup function - lookup, - - // Default configuration for QuicServerSessions - server, - - // True if address verification should be used. - validateAddress, - - // Whether qlog should be enabled for sessions - qlog, - - // Stateless reset token secret (16 byte buffer) - statelessResetSecret, - - // When true, stateless resets will not be sent (default false) - disableStatelessReset, - } = validateQuicSocketOptions(options); - super({ captureRejections: true }); - - const state = this[kInternalState]; - - state.client = client; - state.server = server; - state.lookup = lookup; - - let socketOptions = 0; - if (validateAddress) - socketOptions |= (1 << QUICSOCKET_OPTIONS_VALIDATE_ADDRESS); - - this[kSetHandle]( - new QuicSocketHandle( - socketOptions, - retryTokenTimeout, - maxConnections, - maxConnectionsPerHost, - maxStatelessResetsPerHost, - qlog, - statelessResetSecret, - disableStatelessReset)); - - this.addEndpoint({ ...endpoint, preferred: true }); - } - - [kRejections](err, eventname, ...args) { - switch (eventname) { - case 'session': - const session = args[0]; - session.destroy(err); - process.nextTick(() => { - this.emit('sessionError', err, session); - }); - return; - default: - // Fall through - } - this.destroy(err); - } - - get [kStreamOptions]() { - const state = this[kInternalState]; - return { - highWaterMark: state.highWaterMark, - defaultEncoding: state.defaultEncoding, - ocspHandler: state.ocspHandler, - clientHelloHandler: state.clientHelloHandler, - context: state.serverSecureContext, - }; - } - - [kSetHandle](handle) { - this[kHandle] = handle; - if (handle !== undefined) { - handle[owner_symbol] = this; - this[async_id_symbol] = handle.getAsyncId(); - this[kInternalState].sharedState = - new QuicSocketSharedState(handle.state); - this[kInternalState].blockList = new BlockList(handle.blockList); - } else { - this[kInternalState].sharedState = undefined; - this[kInternalState].blockList = undefined; - } - } - - [kInspect](depth, options) { - const state = this[kInternalState]; - return customInspect(this, { - endpoints: this.endpoints, - sessions: ArrayFrom(state.sessions), - bound: this.bound, - pending: this.pending, - closing: this.closing, - destroyed: this.destroyed, - listening: this.listening, - serverBusy: this.serverBusy, - statelessResetDisabled: this.statelessResetDisabled, - }, depth, options); - } - - [kAddSession](session) { - this[kInternalState].sessions.add(session); - } - - [kRemoveSession](session) { - const state = this[kInternalState]; - state.sessions.delete(session); - if (this.closing && state.sessions.size === 0) - this.destroy(); - } - - [kMaybeBind](options) { - const state = this[kInternalState]; - if (state.bindPromise !== undefined) - return state.bindPromise; - - return state.bindPromise = PromisePrototypeFinally( - this[kBind](options), - () => { - state.bindPromise = undefined; - } - ); - } - - async [kBind](options) { - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - - const state = this[kInternalState]; - if (state.state === kSocketBound) - return; - - const { signal } = { ...options }; - validateAbortSignal(signal, 'options.signal'); - - // If an AbotSignal was passed in, check to make sure it is not already - // aborted before we continue on to do any work. - if (signal && signal.aborted) - throw new lazyDOMException('The operation was aborted', 'AbortError'); - - state.state = kSocketPending; - - const binds = []; - for (const endpoint of state.endpoints) - ArrayPrototypePush(binds, endpoint.bind({ signal })); - - await PromiseAll(binds); - - // It it's possible that the QuicSocket was destroyed while we were - // waiting. Check to make sure. - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket was destroyed'); - - // Any errors from this point on are fatal for the QuicSocket. - try { - // If an AbortSignal is used and it has been triggered, our - // only recourse at this point is to destroy() the QuicSocket. - // Some number of endpoints may have successfully bound, while - // others have not - if (signal && signal.aborted) - throw lazyDOMException('The operation was aborted', 'AbortError'); - - state.state = kSocketBound; - - process.nextTick(() => { - // User code may have run before this so we need to check the - // destroyed state. If it has been destroyed, do nothing. - if (this.destroyed) - return; - try { - this.emit('ready'); - } catch (error) { - this.destroy(error); - } - }); - } catch (error) { - this.destroy(error); - throw error; - } - } - - // Currently only used for testing when the QuicEndpoint is bound immediately. - [kEndpointBound](endpoint) { - const state = this[kInternalState]; - if (state.state === kSocketBound) - return; - state.state = kSocketBound; - - // The ready event indicates that the QuicSocket is ready to be - // used to either listen or connect. No QuicServerSession should - // exist before this event, and all QuicClientSession will remain - // in Initial states until ready is invoked. - process.nextTick(() => { - try { - this.emit('ready'); - } catch (error) { - this.destroy(error); - } - }); - } - - // Called by the C++ internals to notify when server busy status is toggled. - [kServerBusy]() { - const busy = this.serverBusy; - process.nextTick(() => { - try { - this.emit('busy', busy); - } catch (error) { - this[kRejections](error, 'busy', busy); - } - }); - } - - // A QuicSocket will typically bind only to a single local port, but it is - // possible to bind to multiple, even if those use different IP families - // (e.g. IPv4 or IPv6). Calls to addEndpoint() must be made before the - // QuicSocket is bound (e.g. before any calls to listen() or connect()). - addEndpoint(options = {}) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - if (state.state !== kSocketUnbound) - throw new ERR_INVALID_STATE('QuicSocket is already being bound'); - - options = { - lookup: state.lookup, - ...options - }; - - const endpoint = new QuicEndpoint(this, options); - state.endpoints.add(endpoint); - return endpoint; - } - - listen(options) { - const state = this[kInternalState]; - if (state.listenPromise !== undefined) - return state.listenPromise; - - return state.listenPromise = PromisePrototypeFinally( - this[kListen](options), - () => { - state.listenPromise = undefined; - } - ); - } - - async [kListen](options) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - if (this.listening) - throw new ERR_INVALID_STATE('QuicSocket is already listening'); - - options = { - ...state.server, - ...options, - }; - - // The ALPN protocol identifier is strictly required. - const { - alpn, - lookup = state.lookup, - defaultEncoding, - highWaterMark, - ocspHandler, - clientHelloHandler, - transportParams, - } = validateQuicSocketListenOptions(options); - - state.serverSecureContext = - createSecureContext({ - ...options, - minVersion: 'TLSv1.3', - maxVersion: 'TLSv1.3', - }, initSecureContext); - state.highWaterMark = highWaterMark; - state.defaultEncoding = defaultEncoding; - state.alpn = alpn; - state.listenPending = true; - state.ocspHandler = ocspHandler; - state.clientHelloHandler = clientHelloHandler; - - await this[kMaybeBind](); - - // It's possible that the QuicSocket was destroyed or closed while - // the bind was pending. Check for that and handle accordingly. - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket was destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - - const { - ip, - port, - type - } = await resolvePreferredAddress(lookup, transportParams.preferredAddress); - - // It's possible that the QuicSocket was destroyed or closed while - // the preferred address resolution was pending. Check for that and handle - // accordingly. - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket was destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - - const { - rejectUnauthorized = !getAllowUnauthorized(), - requestCert = false, - } = transportParams; - - // Transport Parameters are passed to the C++ side using a shared array. - // These are the transport parameters that will be used when a new - // server QuicSession is established. They are transmitted to the client - // as part of the server's initial TLS handshake. Once they are set, they - // cannot be modified. - setTransportParams(transportParams); - - // When the handle is told to listen, it will begin acting as a QUIC - // server and will emit session events whenever a new QuicServerSession - // is created. - state.listenPending = false; - this[kHandle].listen( - state.serverSecureContext.context, - ip, // Preferred address ip, - type, // Preferred address type, - port, // Preferred address port, - state.alpn, - (rejectUnauthorized ? QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED : 0) | - (requestCert ? QUICSERVERSESSION_OPTION_REQUEST_CERT : 0)); - - process.nextTick(() => { - // It's remotely possible the QuicSocket is be destroyed or closed - // while the nextTick is pending. If that happens, do nothing. - if (this.destroyed || this.closing) - return; - try { - this.emit('listening'); - } catch (error) { - this.destroy(error); - } - }); - } - - async connect(options) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - - options = { - ...state.client, - ...options - }; - - const { - type, - address, - lookup = state.lookup - } = validateQuicSocketConnectOptions(options); - - await this[kMaybeBind](); - - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket was destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - - const { - address: ip - } = await lookup(addressOrLocalhost(address, type), - type === AF_INET6 ? 6 : 4); - - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket was destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - - if (this.blockList.check(ip, type === AF_INET6 ? 'ipv6' : 'ipv4')) - throw new ERR_OPERATION_FAILED(`${ip} failed BlockList check`); - - return new QuicClientSession(this, options, type, ip); - } - - [kEndpointClose](endpoint, error) { - const state = this[kInternalState]; - state.endpoints.delete(endpoint); - process.nextTick(() => { - try { - this.emit('endpointClose', endpoint, error); - } catch (error) { - this.destroy(error); - } - }); - - // If there aren't any more endpoints, the QuicSession - // is no longer usable and needs to be destroyed. - if (state.endpoints.size === 0) { - if (!this.destroyed) - return this.destroy(error); - this[kDestroy](error); - } - } - - // Initiate a Graceful Close of the QuicSocket. - // Existing QuicClientSession and QuicServerSession instances will be - // permitted to close naturally and gracefully on their own. - // The QuicSocket will be immediately closed and freed as soon as there - // are no additional session instances remaining. If there are no - // QuicClientSession or QuicServerSession instances, the QuicSocket - // will be immediately closed. - // - // Returns a Promise that will be resolved once the QuicSocket is - // destroyed. - // - // No additional QuicServerSession instances will be accepted from - // remote peers, and calls to connect() to create QuicClientSession - // instances will fail. The QuicSocket will be otherwise usable in - // every other way. - // - // Once initiated, a graceful close cannot be canceled. The graceful - // close can be interupted, however, by abruptly destroying the - // QuicSocket using the destroy() method. - // - // If close() is called before the QuicSocket has been bound (before - // either connect() or listen() have been called, or the QuicSocket - // is still in the pending state, the QuicSocket is destroyed - // immediately. - close() { - return this[kInternalState].closePromise || this[kClose](); - } - - [kClose]() { - if (this.destroyed) { - return PromiseReject( - new ERR_INVALID_STATE('QuicSocket is already destroyed')); - } - const state = this[kInternalState]; - const promise = deferredClosePromise(state); - - // Tell the underlying QuicSocket C++ object to stop - // listening for new QuicServerSession connections. - // New initial connection packets for currently unknown - // DCID's will be ignored. - if (this[kHandle]) - state.sharedState.serverListening = false; - - // If the QuicSocket is otherwise not bound to the local - // port, or there are not active sessions, destroy the - // QuicSocket immediately and we're done. - if (state.state !== kSocketBound || state.sessions.size === 0) { - this.destroy(); - return promise; - } - - // Otherwise, loop through each of the known sessions and close them. - const reqs = [promise]; - for (const session of state.sessions) { - ArrayPrototypePush(reqs, - PromisePrototypeCatch(session.close(), - (error) => this.destroy(error))); - } - return PromiseAll(reqs); - } - - // Initiate an abrupt close and destruction of the QuicSocket. - // Existing QuicClientSession and QuicServerSession instances will be - // immediately closed. If error is specified, it will be forwarded - // to each of the session instances. - // - // When the session instances are closed, an attempt to send a final - // CONNECTION_CLOSE will be made. - // - // The JavaScript QuicSocket object will be marked destroyed and will - // become unusable. As soon as all pending outbound UDP packets are - // flushed from the QuicSocket's queue, the QuicSocket C++ instance - // will be destroyed and freed from memory. - destroy(error) { - const state = this[kInternalState]; - // If the QuicSocket is already destroyed, do nothing - if (state.state === kSocketDestroyed) - return; - - // Mark the QuicSocket as being destroyed. - state.state = kSocketDestroyed; - this[kHandle].stats[IDX_QUIC_SOCKET_STATS_DESTROYED_AT] = - process.hrtime.bigint(); - state.stats = new BigInt64Array(this[kHandle].stats); - - // Immediately close any sessions that may be remaining. - // If the udp socket is in a state where it is able to do so, - // a final attempt to send CONNECTION_CLOSE frames for each - // closed session will be made. - for (const session of state.sessions) - session.destroy(error); - - // If there aren't any QuicEndpoints to clean up, skip - // directly to the end to emit the error and close events. - if (state.endpoints.size === 0) - return this[kDestroy](error); - - // Otherwise, the QuicSocket will be destroyed once all - // QuicEndpoints are destroyed. See [kEndpointClose]. - for (const endpoint of state.endpoints) - endpoint.destroy(error); - } - - [kDestroy](error) { - const state = this[kInternalState]; - if (error) { - if (typeof state.closePromiseReject === 'function') - state.closePromiseReject(error); - process.nextTick(FunctionPrototypeBind(emit, this, 'error', error)); - } else if (typeof state.closePromiseResolve === 'function') { - state.closePromiseResolve(); - } - process.nextTick(FunctionPrototypeBind(emit, this, 'close')); - } - - ref() { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - for (const endpoint of state.endpoints) - endpoint.ref(); - return this; - } - - unref() { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - for (const endpoint of state.endpoints) - endpoint.unref(); - return this; - } - - get blockList() { - return this[kInternalState]?.blockList; - } - - get endpoints() { - return ArrayFrom(this[kInternalState].endpoints); - } - - get serverSecureContext() { - return this[kInternalState].serverSecureContext; - } - - // True if at least one associated QuicEndpoint has been successfully - // bound to a local UDP port. - get bound() { - return this[kInternalState].state === kSocketBound; - } - - // True if graceful close has been initiated by calling close() - get closing() { - return this[kInternalState].closePromise !== undefined; - } - - // True if the QuicSocket has been destroyed and is no longer usable - get destroyed() { - return this[kInternalState].state === kSocketDestroyed; - } - - // True if listen() has been called successfully - get listening() { - return Boolean(this[kInternalState].sharedState?.serverListening); - } - - // True if the QuicSocket is currently waiting on at least one - // QuicEndpoint to succesfully bind.g - get pending() { - return this[kInternalState].state === kSocketPending; - } - - // Marking a server as busy will cause all new - // connection attempts to fail with a SERVER_BUSY CONNECTION_CLOSE. - set serverBusy(on) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - validateBoolean(on, 'on'); - if (state.sharedState.serverBusy !== on) { - state.sharedState.serverBusy = on; - this[kServerBusy](); - } - } - - get serverBusy() { - return Boolean(this[kInternalState].sharedState?.serverBusy); - } - - set statelessResetDisabled(on) { - const state = this[kInternalState]; - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - validateBoolean(on, 'on'); - if (state.sharedState.statelessResetDisabled !== on) - state.sharedState.statelessResetDisabled = on; - } - - get statelessResetDisabled() { - return Boolean(this[kInternalState].sharedState?.statelessResetDisabled); - } - - get duration() { - const end = getStats(this, IDX_QUIC_SOCKET_STATS_DESTROYED_AT) || - process.hrtime.bigint(); - return Number(end - getStats(this, IDX_QUIC_SOCKET_STATS_CREATED_AT)); - } - - get boundDuration() { - const end = getStats(this, IDX_QUIC_SOCKET_STATS_DESTROYED_AT) || - process.hrtime.bigint(); - return Number(end - getStats(this, IDX_QUIC_SOCKET_STATS_BOUND_AT)); - } - - get listenDuration() { - const end = getStats(this, IDX_QUIC_SOCKET_STATS_DESTROYED_AT) || - process.hrtime.bigint(); - return Number(end - getStats(this, IDX_QUIC_SOCKET_STATS_LISTEN_AT)); - } - - get bytesReceived() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED)); - } - - get bytesSent() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_BYTES_SENT)); - } - - get packetsReceived() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED)); - } - - get packetsSent() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_PACKETS_SENT)); - } - - get packetsIgnored() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED)); - } - - get serverSessions() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS)); - } - - get clientSessions() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS)); - } - - get statelessResetCount() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT)); - } - - get serverBusyCount() { - return Number(getStats(this, IDX_QUIC_SOCKET_STATS_SERVER_BUSY_COUNT)); - } - - // Diagnostic packet loss is a testing mechanism that allows simulating - // pseudo-random packet loss for rx or tx. The value specified for each - // option is a number between 0 and 1 that identifies the possibility of - // packet loss in the given direction. - setDiagnosticPacketLoss(options) { - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - const { - rx = 0.0, - tx = 0.0 - } = { ...options }; - validateNumber( - rx, - 'options.rx', - /* min */ 0.0, - /* max */ 1.0); - validateNumber( - tx, - 'options.tx', - /* min */ 0.0, - /* max */ 1.0); - if ((rx > 0.0 || tx > 0.0) && !diagnosticPacketLossWarned) { - diagnosticPacketLossWarned = true; - process.emitWarning( - 'QuicSocket diagnostic packet loss is enabled. Received or ' + - 'transmitted packets will be randomly ignored to simulate ' + - 'network packet loss.'); - } - this[kHandle].setDiagnosticPacketLoss(rx, tx); - } -} - -class QuicSession extends EventEmitter { - [kInternalState] = { - alpn: undefined, - cipher: undefined, - cipherVersion: undefined, - clientHelloHandler: undefined, - closeCode: NGTCP2_NO_ERROR, - closeFamily: QUIC_ERROR_APPLICATION, - closePromise: undefined, - closePromiseResolve: undefined, - closePromiseReject: undefined, - destroyed: false, - earlyData: false, - handshakeComplete: false, - handshakeCompletePromise: undefined, - handshakeCompletePromiseResolve: undefined, - handshakeCompletePromiseReject: undefined, - idleTimeout: false, - maxPacketLength: NGTCP2_DEFAULT_MAX_PKTLEN, - ocspHandler: undefined, - servername: undefined, - socket: undefined, - silentClose: false, - statelessReset: false, - stats: undefined, - streams: new Map(), - verifyErrorReason: undefined, - verifyErrorCode: undefined, - handshakeAckHistogram: undefined, - handshakeContinuationHistogram: undefined, - highWaterMark: undefined, - defaultEncoding: undefined, - state: undefined, - qlogStream: undefined, - }; - - constructor(socket, options) { - const { - alpn, - servername, - highWaterMark, - defaultEncoding, - ocspHandler, - clientHelloHandler, - } = options; - super({ captureRejections: true }); - this.on('newListener', onNewListener); - this.on('removeListener', onRemoveListener); - const state = this[kInternalState]; - state.socket = socket; - state.servername = servername; - state.alpn = alpn; - state.highWaterMark = highWaterMark; - state.defaultEncoding = defaultEncoding; - state.ocspHandler = ocspHandler; - state.clientHelloHandler = clientHelloHandler; - socket[kAddSession](this); - } - - [kRejections](err, eventname, ...args) { - this.destroy(err); - } - - // Used to get the configured options for peer initiated QuicStream - // instances. - get [kStreamOptions]() { - const state = this[kInternalState]; - return { - highWaterMark: state.highWaterMark, - defaultEncoding: state.defaultEncoding, - }; - } - - [kSetQLogStream](stream) { - const state = this[kInternalState]; - state.qlogStream = stream; - process.nextTick(() => { - try { - this.emit('qlog', state.qlogStream); - } catch (error) { - this.destroy(error); - } - }); - } - - [kHandshakeComplete]() { - const state = this[kInternalState]; - if (state.handshakeComplete) - return PromiseResolve(); - - if (state.handshakeCompletePromise !== undefined) - return state.handshakeCompletePromise; - - state.handshakeCompletePromise = PromisePrototypeFinally( - new Promise((resolve, reject) => { - state.handshakeCompletePromiseResolve = resolve; - state.handshakeCompletePromiseReject = reject; - }), - () => { - state.handshakeCompletePromise = undefined; - state.handshakeCompletePromiseReject = undefined; - state.handshakeCompletePromiseResolve = undefined; - } - ); - - return state.handshakeCompletePromise; - } - - // Sets the internal handle for the QuicSession instance. For - // server QuicSessions, this is called immediately as the - // handle is created before the QuicServerSession JS object. - // For client QuicSession instances, the connect() method - // must first perform DNS resolution on the provided address - // before the underlying QuicSession handle can be created. - [kSetHandle](handle) { - const state = this[kInternalState]; - this[kHandle] = handle; - if (handle !== undefined) { - handle[owner_symbol] = this; - state.state = new QuicSessionSharedState(handle.state); - state.handshakeAckHistogram = new Histogram(handle.ack); - state.handshakeContinuationHistogram = new Histogram(handle.rate); - state.state.ocspEnabled = state.ocspHandler !== undefined; - state.state.clientHelloEnabled = state.clientHelloHandler !== undefined; - if (handle.qlogStream !== undefined) { - this[kSetQLogStream](handle.qlogStream); - handle.qlogStream = undefined; - } - } else { - if (state.handshakeAckHistogram) - state.handshakeAckHistogram[kDestroyHistogram](); - if (state.handshakeContinuationHistogram) - state.handshakeContinuationHistogram[kDestroyHistogram](); - } - } - - // Called when a client QuicSession instance receives a version - // negotiation packet from the server peer. The client QuicSession - // is destroyed immediately. This is not called at all for server - // QuicSessions. - [kVersionNegotiation](version, requestedVersions, supportedVersions) { - const err = - new ERR_QUIC_VERSION_NEGOTIATION( - version, - requestedVersions, - supportedVersions); - err.detail = { - version, - requestedVersions, - supportedVersions, - }; - this.destroy(err); - } - - // Closes the specified stream with the given code. The - // QuicStream object will be destroyed. - [kStreamClose](id, code) { - const stream = this[kInternalState].streams.get(id); - if (stream === undefined) - return; - stream[kDestroy](code); - } - - [kStreamReset](id, code) { - const stream = this[kInternalState].streams.get(id); - if (stream === undefined) - return; - - stream[kStreamReset](code); - } - - // Delivers a block of headers to the appropriate QuicStream - // instance. This will only be called if the ALPN selected - // is known to support headers. - [kHeaders](id, headers, kind, push_id) { - const stream = this[kInternalState].streams.get(id); - if (stream === undefined) - return; - - stream[kHeaders](headers, kind, push_id); - } - - [kInspect](depth, options) { - const state = this[kInternalState]; - return customInspect(this, { - alpn: state.alpn, - cipher: this.cipher, - closing: this.closing, - closeCode: this.closeCode, - destroyed: this.destroyed, - earlyData: state.earlyData, - maxStreams: this.maxStreams, - servername: this.servername, - streams: state.streams.size, - }, depth, options); - } - - [kSetSocket](socket, natRebinding = false) { - this[kInternalState].socket = socket; - if (socket !== undefined) - this[kHandle].setSocket(socket[kHandle], natRebinding); - } - - // Called at the completion of the TLS handshake for the local peer - [kHandshake]( - servername, - alpn, - cipher, - cipherVersion, - maxPacketLength, - verifyErrorReason, - verifyErrorCode, - earlyData) { - const state = this[kInternalState]; - state.handshakeComplete = true; - state.servername = servername; - state.alpn = alpn; - state.cipher = cipher; - state.cipherVersion = cipherVersion; - state.maxPacketLength = maxPacketLength; - state.verifyErrorReason = verifyErrorReason; - state.verifyErrorCode = verifyErrorCode; - state.earlyData = earlyData; - - if (!this[kHandshakePost]()) { - if (typeof state.handshakeCompletePromiseReject === 'function') { - state.handshakeCompletePromiseReject( - new ERR_OPERATION_FAILED('Handshake failed')); - } - return; - } - - if (typeof state.handshakeCompletePromiseResolve === 'function') - state.handshakeCompletePromiseResolve(); - - process.nextTick(() => { - try { - this.emit('secure', servername, alpn, this.cipher); - } catch (error) { - this.destroy(error); - } - }); - } - - // Non-op for the default case. QuicClientSession - // overrides this with some client-side specific - // checks - [kHandshakePost]() { - return true; - } - - [kRemoveStream](stream) { - this[kInternalState].streams.delete(stream.id); - this[kMaybeDestroy](); - } - - [kAddStream](id, stream) { - this[kInternalState].streams.set(id, stream); - } - - // Called when a client QuicSession has opted to use the - // server provided preferred address. This is a purely - // informationational notification. It is not called on - // server QuicSession instances. - [kUsePreferredAddress](address) { - process.nextTick(() => { - try { - this.emit('usePreferredAddress', address); - } catch (error) { - this.destroy(error); - } - }); - } - - close() { - return this[kInternalState].closePromise || this[kClose](); - } - - [kClose]() { - if (this.destroyed) { - return PromiseReject( - new ERR_INVALID_STATE('QuicSession is already destroyed')); - } - const promise = deferredClosePromise(this[kInternalState]); - if (!this[kMaybeDestroy]()) { - this[kHandle].gracefulClose(); - } - return promise; - } - - get closing() { - return this[kInternalState].closePromise !== undefined; - } - - // The QuicSession will be destroyed if close() has been - // called and there are no remaining streams - [kMaybeDestroy]() { - const state = this[kInternalState]; - if (this.closing && state.streams.size === 0) { - this.destroy(); - return true; - } - return false; - } - - // Causes the QuicSession to be immediately destroyed, but with - // additional metadata set. - [kDestroy](code, family, silent, statelessReset) { - const state = this[kInternalState]; - state.closeCode = code; - state.closeFamily = family; - state.silentClose = silent; - state.statelessReset = statelessReset; - this.destroy(); - } - - // Destroying synchronously shuts down and frees the - // QuicSession immediately, even if there are still open - // streams. - // - // Unless we're in the middle of a silent close, a - // CONNECTION_CLOSE packet will be sent to the connected - // peer and the session will be immediately destroyed. - // - // If destroy is called with an error argument, the - // 'error' event is emitted on next tick. - // - // Once destroyed, and after the 'error' event (if any), - // the 'close' event is emitted on next tick. - destroy(error) { - const state = this[kInternalState]; - // Destroy can only be called once. Multiple calls will be ignored - if (state.destroyed) - return; - state.destroyed = true; - - state.idleTimeout = Boolean(this[kInternalState].state?.idleTimeout); - - // Destroy any remaining streams immediately. - for (const stream of state.streams.values()) - stream.destroy(error); - - this.removeListener('newListener', onNewListener); - this.removeListener('removeListener', onRemoveListener); - - const handle = this[kHandle]; - this[kHandle] = undefined; - if (handle !== undefined) { - // Copy the stats for use after destruction - handle.stats[IDX_QUIC_SESSION_STATS_DESTROYED_AT] = - process.hrtime.bigint(); - state.stats = new BigInt64Array(handle.stats); - - // Destroy the underlying QuicSession handle - handle.destroy(state.closeCode, state.closeFamily); - } - - // Remove the QuicSession JavaScript object from the - // associated QuicSocket. - state.socket[kRemoveSession](this); - state.socket = undefined; - - // If we are destroying with an error, schedule the - // error to be emitted on process.nextTick. - if (error) { - if (typeof state.closePromiseReject === 'function') - state.closePromiseReject(error); - process.nextTick(FunctionPrototypeBind(emit, this, 'error', error)); - } else if (typeof state.closePromiseResolve === 'function') - state.closePromiseResolve(); - - if (typeof state.handshakeCompletePromiseReject === 'function') { - state.handshakeCompletePromiseReject( - new ERR_OPERATION_FAILED('Handshake failed')); - } - - process.nextTick(FunctionPrototypeBind(emit, this, 'close')); - } - - // For server QuicSession instances, true if earlyData is - // enabled. For client QuicSessions, true only if session - // resumption is used and early data was accepted during - // the TLS handshake. The value is set only after the - // TLS handshake is completed (immeditely before the - // secure event is emitted) - get usingEarlyData() { - return this[kInternalState].earlyData; - } - - get maxStreams() { - let bidi = 0; - let uni = 0; - if (this[kHandle]) { - bidi = this[kInternalState].state.maxStreamsBidi; - uni = this[kInternalState].state.maxStreamsUni; - } - return { bidi, uni }; - } - - get qlog() { - return this[kInternalState].qlogStream; - } - - get address() { - return this[kInternalState].socket?.address || {}; - } - - get maxDataLeft() { - return Number(this[kHandle] ? this[kInternalState].state.maxDataLeft : 0); - } - - get bytesInFlight() { - return Number(this[kHandle] ? this[kInternalState].state.bytesInFlight : 0); - } - - get blockCount() { - return Number( - this[kHandle]?.stats[IDX_QUIC_SESSION_STATS_BLOCK_COUNT] || 0); - } - - get authenticated() { - // Specifically check for null. Undefined means the check has not - // been performed yet, another other value other than null means - // there was an error - return this[kInternalState].verifyErrorReason == null; - } - - get authenticationError() { - if (this.authenticated) - return undefined; - const state = this[kInternalState]; - // eslint-disable-next-line no-restricted-syntax - const err = new Error(state.verifyErrorReason); - const code = 'ERR_QUIC_VERIFY_' + state.verifyErrorCode; - err.name = `Error [${code}]`; - err.code = code; - return err; - } - - get remoteAddress() { - const out = {}; - if (this[kHandle]) - this[kHandle].getRemoteAddress(out); - return out; - } - - get handshakeComplete() { - return this[kInternalState].handshakeComplete; - } - - get handshakeConfirmed() { - return this[kHandle] ? - this[kInternalState].state.handshakeConfirmed : - false; - } - - get idleTimeout() { - return this[kInternalState].idleTimeout; - } - - get alpnProtocol() { - return this[kInternalState].alpn; - } - - get cipher() { - if (!this.handshakeComplete) - return {}; - const state = this[kInternalState]; - return { - name: state.cipher, - version: state.cipherVersion, - }; - } - - getCertificate() { - return this[kHandle] ? - translatePeerCertificate(this[kHandle].getCertificate() || {}) : {}; - } - - getPeerCertificate(detailed = false) { - return this[kHandle] ? - translatePeerCertificate( - this[kHandle].getPeerCertificate(detailed) || {}) : {}; - } - - ping() { - if (this.destroyed) { - throw new ERR_INVALID_STATE( - `${this.constructor.name} is already destroyed`); - } - this[kHandle].ping(); - } - - get servername() { - return this[kInternalState].servername; - } - - get destroyed() { - return this[kInternalState].destroyed; - } - - get closeCode() { - const state = this[kInternalState]; - return { - code: state.closeCode, - family: state.closeFamily, - silent: state.silentClose, - }; - } - - get socket() { - return this[kInternalState].socket; - } - - get statelessReset() { - return this[kInternalState].statelessReset; - } - - async openStream(options) { - if (this.destroyed) { - throw new ERR_INVALID_STATE( - `${this.constructor.name} is already destroyed`); - } - if (this.closing) { - throw new ERR_INVALID_STATE( - `${this.constructor.name} is closing`); - } - - const { - halfOpen, // Unidirectional or Bidirectional - highWaterMark, - defaultEncoding, - } = validateQuicStreamOptions(options); - - const stream = new QuicStream({ - highWaterMark, - defaultEncoding, - readable: !halfOpen - }, this); - - await once(stream, kReady); - - return stream; - } - - get duration() { - const end = getStats(this, IDX_QUIC_SESSION_STATS_DESTROYED_AT) || - process.hrtime.bigint(); - return Number(end - getStats(this, IDX_QUIC_SESSION_STATS_CREATED_AT)); - } - - get handshakeDuration() { - const end = - this.handshakeComplete ? - getStats(this, IDX_QUIC_SESSION_STATS_HANDSHAKE_COMPLETED_AT) : - process.hrtime.bigint(); - return Number( - end - getStats(this, IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT)); - } - - get bytesReceived() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_BYTES_RECEIVED)); - } - - get bytesSent() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_BYTES_SENT)); - } - - get bidiStreamCount() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT)); - } - - get uniStreamCount() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT)); - } - - get maxInFlightBytes() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT)); - } - - get lossRetransmitCount() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT)); - } - - get ackDelayRetransmitCount() { - return Number( - getStats(this, IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT)); - } - - get peerInitiatedStreamCount() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT)); - } - - get selfInitiatedStreamCount() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT)); - } - - get keyUpdateCount() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT)); - } - - get minRTT() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_MIN_RTT)); - } - - get latestRTT() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_LATEST_RTT)); - } - - get smoothedRTT() { - return Number(getStats(this, IDX_QUIC_SESSION_STATS_SMOOTHED_RTT)); - } - - updateKey() { - const state = this[kInternalState]; - // Initiates a key update for the connection. - if (this.destroyed) { - throw new ERR_INVALID_STATE( - `${this.constructor.name} is already destroyed`); - } - if (this.closing) { - throw new ERR_INVALID_STATE( - `${this.constructor.name} is closing`); - } - if (!state.handshakeConfirmed) - throw new ERR_INVALID_STATE('Handshake is not yet confirmed'); - return this[kHandle].updateKey(); - } - - get handshakeAckHistogram() { - return this[kInternalState].handshakeAckHistogram; - } - - get handshakeContinuationHistogram() { - return this[kInternalState].handshakeContinuationHistogram; - } - - [kRemoveFromSocket]() { - return this[kHandle].removeFromSocket(); - } -} -class QuicServerSession extends QuicSession { - [kInternalServerState] = { - context: undefined - }; - - constructor(socket, handle, options) { - const { - highWaterMark, - defaultEncoding, - ocspHandler, - clientHelloHandler, - context, - } = options; - super(socket, { - highWaterMark, - defaultEncoding, - ocspHandler, - clientHelloHandler - }); - this[kInternalServerState].context = context; - this[kSetHandle](handle); - } - - // Called only when a clientHello event handler is registered. - // Allows user code an opportunity to interject into the start - // of the TLS handshake. - async [kClientHello](alpn, servername, ciphers) { - const internalState = this[kInternalState]; - return internalState.clientHelloHandler?.(alpn, servername, ciphers); - } - - async [kHandleOcsp](servername) { - const internalState = this[kInternalState]; - const { context } = this[kInternalServerState]; - if (!internalState.ocspHandler || !context) return undefined; - return internalState.ocspHandler('request', { - servername, - certificate: context.context.getCertificate(), - issuer: context.context.getIssuer() - }); - } - - get allowEarlyData() { return false; } -} - -class QuicClientSession extends QuicSession { - [kInternalClientState] = { - allowEarlyData: false, - handshakeStarted: false, - minDHSize: undefined, - secureContext: undefined, - }; - - constructor(socket, options, type, ip) { - const sc_options = { - ...options, - minVersion: 'TLSv1.3', - maxVersion: 'TLSv1.3', - }; - const { - autoStart, - alpn, - dcid, - minDHSize, - ocspHandler, - port, - preferredAddressPolicy, - remoteTransportParams, - servername, - sessionTicket, - verifyHostnameIdentity, - qlog, - highWaterMark, - defaultEncoding, - } = validateQuicClientSessionOptions(options); - - if (!verifyHostnameIdentity && !warnedVerifyHostnameIdentity) { - warnedVerifyHostnameIdentity = true; - process.emitWarning( - 'QUIC hostname identity verification is disabled. This violates QUIC ' + - 'specification requirements and reduces security. Hostname identity ' + - 'verification should only be disabled for debugging purposes.' - ); - } - - super(socket, { - servername, - alpn, - highWaterMark, - defaultEncoding, - ocspHandler - }); - const state = this[kInternalClientState]; - state.handshakeStarted = autoStart; - state.minDHSize = minDHSize; - - state.secureContext = - createSecureContext( - sc_options, - initSecureContextClient); - - const transportParams = validateTransportParams(options); - - state.allowEarlyData = - remoteTransportParams !== undefined && - sessionTicket !== undefined; - - setTransportParams(transportParams); - - const handle = - _createClientSession( - this.socket[kHandle], - type, - ip, - port, - state.secureContext.context, - this.servername || ip, - remoteTransportParams, - sessionTicket, - dcid, - preferredAddressPolicy, - this.alpnProtocol, - (verifyHostnameIdentity ? - QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY : 0) | - (ocspHandler !== undefined ? - QUICCLIENTSESSION_OPTION_REQUEST_OCSP : 0), - qlog, - autoStart); - - // If handle is a number, creating the session failed. - if (typeof handle === 'number') { - switch (handle) { - case ERR_FAILED_TO_CREATE_SESSION: - throw new ERR_QUIC_FAILED_TO_CREATE_SESSION(); - case ERR_INVALID_REMOTE_TRANSPORT_PARAMS: - throw new ERR_QUIC_INVALID_REMOTE_TRANSPORT_PARAMS(); - case ERR_INVALID_TLS_SESSION_TICKET: - throw new ERR_QUIC_INVALID_TLS_SESSION_TICKET(); - default: - throw new ERR_OPERATION_FAILED(`Unspecified reason [${handle}]`); - } - } - - this[kSetHandle](handle); - } - - [kHandshakePost]() { - const { type, size } = this.ephemeralKeyInfo; - if (type === 'DH' && size < this[kInternalClientState].minDHSize) { - this.destroy(new ERR_TLS_DH_PARAM_SIZE(size)); - return false; - } - return true; - } - - async [kHandleOcsp](data) { - const internalState = this[kInternalState]; - return internalState.ocspHandler?.('response', { data }); - } - - get allowEarlyData() { - return this[kInternalClientState].allowEarlyData; - } - - get handshakeStarted() { - return this[kInternalClientState].handshakeStarted; - } - - startHandshake() { - const state = this[kInternalClientState]; - if (this.destroyed) { - throw new ERR_INVALID_STATE( - `${this.constructor.name} is already destroyed`); - } - if (state.handshakeStarted) - return; - state.handshakeStarted = true; - this[kHandle].startHandshake(); - } - - get ephemeralKeyInfo() { - return this[kHandle] !== undefined ? - this[kHandle].getEphemeralKeyInfo() : - {}; - } - - async setSocket(socket, natRebinding = false) { - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicClientSession is already destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicClientSession is closing'); - if (!(socket instanceof QuicSocket)) - throw new ERR_INVALID_ARG_TYPE('socket', 'QuicSocket', socket); - if (socket.destroyed) - throw new ERR_INVALID_STATE('QuicSocket is already destroyed'); - if (socket.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - if (typeof natRebinding !== 'boolean') - throw new ERR_INVALID_ARG_TYPE('natRebinding', 'boolean', true); - - await socket[kMaybeBind](); - - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicClientSession was destroyed'); - if (this.closing) - throw new ERR_INVALID_STATE('QuicClientSession is closing'); - if (socket.destroyed) - throw new ERR_INVALID_STATE('QuicSocket was destroyed'); - if (socket.closing) - throw new ERR_INVALID_STATE('QuicSocket is closing'); - - if (this.socket) { - this.socket[kRemoveSession](this); - this[kSetSocket](undefined); - } - socket[kAddSession](this); - this[kSetSocket](socket, natRebinding); - } -} - -function streamOnResume() { - if (!this.destroyed && this.readable) - this[kHandle].readStart(); -} - -function streamOnPause() { - if (!this.destroyed) - this[kHandle].readStop(); -} -class QuicStream extends Duplex { - [kInternalState] = { - closed: false, - closePromise: undefined, - closePromiseReject: undefined, - closePromiseResolve: undefined, - defaultEncoding: undefined, - didRead: false, - id: undefined, - highWaterMark: undefined, - push_id: undefined, - resetCode: undefined, - session: undefined, - dataRateHistogram: undefined, - dataSizeHistogram: undefined, - dataAckHistogram: undefined, - ready: false, - sharedState: undefined, - stats: undefined, - }; - - constructor(options, session, handle, push_id) { - const { - highWaterMark, - defaultEncoding, - readable = true, - writable = true, - } = options; - - super({ - readable, - writable, - highWaterMark, - defaultEncoding, - allowHalfOpen: true, - decodeStrings: true, - emitClose: true, - autoDestroy: true, - captureRejections: true, - }); - const state = this[kInternalState]; - state.highWaterMark = highWaterMark; - state.defaultEncoding = defaultEncoding; - state.session = session; - state.push_id = push_id; - this._readableState.readingMore = true; - this.on('pause', streamOnPause); - - if (handle !== undefined) - this[kSetHandle](handle); - } - - async _construct(callback) { - try { - if (this[kInternalState].ready) - return callback(); - - // Handle is already initialized - const unidirectional = !this.readable; - - await this.session[kHandshakeComplete](); - - if (this.destroyed) { - throw new ERR_INVALID_STATE('QuicStream was destroyed'); - } - if (this.session.destroyed) { - throw new ERR_INVALID_STATE( - `${this.session.constructor.name} was destroyed`); - } - if (this.session.closing) { - throw new ERR_INVALID_STATE( - `${this.session.constructor.name} is closing`); - } - - const handle = - unidirectional ? - _openUnidirectionalStream(this.session[kHandle]) : - _openBidirectionalStream(this.session[kHandle]); - - if (handle === undefined) - throw new ERR_OPERATION_FAILED('Unable to create QuicStream'); - - this[kSetHandle](handle); - callback(); - } catch (error) { - callback(error); - } - } - - // Set handle is called once the QuicSession has been able - // to complete creation of the internal QuicStream handle. - // This will happen only after the QuicSession's own - // internal handle has been created. The QuicStream object - // is still minimally usable before this but any data - // written will be buffered until kSetHandle is called. - [kSetHandle](handle) { - const state = this[kInternalState]; - const current = this[kHandle]; - this[kHandle] = handle; - if (handle !== undefined) { - handle.onread = onStreamRead; - handle[owner_symbol] = this; - this[async_id_symbol] = handle.getAsyncId(); - state.id = handle.id(); - state.dataRateHistogram = new Histogram(handle.rate); - state.dataSizeHistogram = new Histogram(handle.size); - state.dataAckHistogram = new Histogram(handle.ack); - state.sharedState = new QuicStreamSharedState(handle.state); - state.session[kAddStream](state.id, this); - state.ready = true; - this.emit(kReady); - } else { - if (current !== undefined) { - current.stats[IDX_QUIC_STREAM_STATS_DESTROYED_AT] = - process.hrtime.bigint(); - state.stats = new BigInt64Array(current.stats); - } - state.sharedState = undefined; - if (state.dataRateHistogram) - state.dataRateHistogram[kDestroyHistogram](); - if (state.dataSizeHistogram) - state.dataSizeHistogram[kDestroyHistogram](); - if (state.dataAckHistogram) - state.dataAckHistogram[kDestroyHistogram](); - } - } - - [kStreamReset](code) { - // Receiving a reset from the peer indicates that it is no - // longer sending any data, we can safely close the readable - // side of the Duplex here. - this[kInternalState].resetCode = code; - this.push(null); - this.read(); - } - - [kClose]() { - const state = this[kInternalState]; - - if (this.destroyed) { - return PromiseReject( - new ERR_INVALID_STATE('QuicStream is already destroyed')); - } - - const promise = deferredClosePromise(state); - if (this.readable) { - this.push(null); - this.read(); - } - - if (this.writable) { - this.end(); - } - - // TODO(@jasnell): Investigate later if a Promise version - // of finished() can work here instead. - return promise; - } - - close() { - return this[kInternalState].closePromise || this[kClose](); - } - - _destroy(error, callback) { - const state = this[kInternalState]; - const handle = this[kHandle]; - this[kSetHandle](); - if (handle !== undefined) - handle.destroy(); - state.session[kRemoveStream](this); - - if (error && typeof state.closePromiseReject === 'function') - state.closePromiseReject(error); - else if (typeof state.closePromiseResolve === 'function') - state.closePromiseResolve(); - - // TODO(@jasnell): Investigate how we can eliminate the nextTick here - process.nextTick(() => callback(error)); - } - - [kDestroy](code) { - // TODO(@jasnell): If code is non-zero, and stream is not otherwise - // naturally shutdown, then we should destroy with an error. - - // Put the QuicStream into detached mode before calling destroy - this[kSetHandle](); - this.destroy(); - } - - [kHeaders](headers, kind, push_id) { - // TODO(@jasnell): Convert the headers into a proper object - let name; - switch (kind) { - case QUICSTREAM_HEADERS_KIND_NONE: - // Fall through - case QUICSTREAM_HEADERS_KIND_INITIAL: - name = 'initialHeaders'; - break; - case QUICSTREAM_HEADERS_KIND_INFORMATIONAL: - name = 'informationalHeaders'; - break; - case QUICSTREAM_HEADERS_KIND_TRAILING: - name = 'trailingHeaders'; - break; - case QUICSTREAM_HEADERS_KIND_PUSH: - name = 'pushHeaders'; - break; - default: - assert.fail('Invalid headers kind'); - } - process.nextTick(FunctionPrototypeBind(emit, this, name, headers, push_id)); - } - - [kAfterAsyncWrite]({ bytes }) { - // There's currently nothing we need to do here. We have - // to have this but it's a non-op - } - - [kUpdateTimer]() { - // Timeout is implemented in the QuicSession at the native - // layer. We have to have this here but it's a non-op - } - - [kTrackWriteState](stream, bytes) { - // There's currently nothing to do here. - } - - [kInspect](depth, options) { - const direction = this.bidirectional ? 'bidirectional' : 'unidirectional'; - const initiated = this.serverInitiated ? 'server' : 'client'; - return customInspect(this, { - id: this[kInternalState].id, - detached: this.detached, - direction, - initiated, - writableState: this._writableState, - readableState: this._readableState - }, depth, options); - } - - get detached() { - // The QuicStream is detached if it is yet destroyed - // but the underlying handle is undefined. While in - // detached mode, the QuicStream may still have - // data pending in the read queue, but writes will - // not be permitted. - return this[kHandle] === undefined; - } - - get serverInitiated() { - return !!(this[kInternalState].id & 0b01); - } - - get clientInitiated() { - return !this.serverInitiated; - } - - get unidirectional() { - return !!(this[kInternalState].id & 0b10); - } - - get bidirectional() { - return !this.unidirectional; - } - - [kWriteGeneric](writev, data, encoding, cb) { - if (this.destroyed || this.detached) - return; - - this[kUpdateTimer](); - const req = (writev) ? - writevGeneric(this, data, cb) : - writeGeneric(this, data, encoding, cb); - - this[kTrackWriteState](this, req.bytes); - } - - _write(data, encoding, cb) { - this[kWriteGeneric](false, data, encoding, cb); - } - - _writev(data, cb) { - this[kWriteGeneric](true, data, '', cb); - } - - // Called when the last chunk of data has been - // acknowledged by the peer and end has been - // called. By calling shutdown, we're telling - // the native side that no more data will be - // coming so that a fin stream packet can be - // sent, allowing any remaining final stream - // frames to be sent if necessary. - // - // When end() is called, we set the writeEnded - // flag so that we can know earlier when there - // is not going to be any more data being written - // but that is only used when end() is called - // with a final chunk to write. - _final(cb) { - if (!this.detached) { - const state = this[kInternalState]; - if (state.sharedState?.finSent) - return cb(); - const handle = this[kHandle]; - const req = new ShutdownWrap(); - req.oncomplete = () => { - req.handle = undefined; - cb(); - }; - req.handle = handle; - if (handle.shutdown(req) === 1) - return req.oncomplete(); - return; - } - return cb(); - } - - end(...args) { - if (!this.destroyed) { - if (!this.detached) - this[kInternalState].sharedState.writeEnded = true; - ReflectApply(super.end, this, args); - } - return this; - } - - _read(nread) { - if (this.destroyed) { - this.push(null); - return; - } - const state = this[kInternalState]; - if (!state.didRead) { - this._readableState.readingMore = false; - state.didRead = true; - } - - FunctionPrototypeCall(streamOnResume, this); - } - - sendFile(path, options = {}) { - if (this.detached) - throw new ERR_INVALID_STATE('Unable to send file'); - fs.open(path, 'r', - FunctionPrototypeBind(QuicStream[kOnFileOpened], this, options)); - } - - static [kOnFileOpened](options, err, fd) { - const onError = options.onError; - if (err) { - if (onError) { - this.close(); - onError(err); - } else { - this.destroy(err); - } - return; - } - - if (this.destroyed || this.closed) { - fs.close(fd, assert.ifError); - return; - } - - this.sendFD(fd, options, true); - } - - sendFD(fd, { offset = -1, length = -1 } = {}, ownsFd = false) { - if (this.destroyed || this[kInternalState].closed) - return; - - if (this.detached) - throw new ERR_INVALID_STATE('Unable to send file descriptor'); - - validateInteger(offset, 'options.offset', /* min */ -1); - validateInteger(length, 'options.length', /* min */ -1); - - if (fd instanceof fsPromisesInternal.FileHandle) - fd = fd.fd; - else if (typeof fd !== 'number') - throw new ERR_INVALID_ARG_TYPE('fd', ['number', 'FileHandle'], fd); - - this[kUpdateTimer](); - this.ownsFd = ownsFd; - - defaultTriggerAsyncIdScope(this[async_id_symbol], - QuicStream[kStartFilePipe], - this, fd, offset, length); - } - - static [kStartFilePipe](stream, fd, offset, length) { - const handle = new FileHandle(fd, offset, length); - handle.onread = QuicStream[kOnPipedFileHandleRead]; - handle.stream = stream; - - const pipe = new StreamPipe(handle, stream[kHandle]); - pipe.onunpipe = QuicStream[kOnFileUnpipe]; - pipe.start(); - - // Exact length of the file doesn't matter here, since the - // stream is closing anyway - just use 1 to signify that - // a write does exist - stream[kTrackWriteState](stream, 1); - } - - static [kOnFileUnpipe]() { // Called on the StreamPipe instance. - const stream = this.sink[owner_symbol]; - if (stream.ownsFd) - PromisePrototypeCatch(this.source.close(), - FunctionPrototypeBind(stream.destroy, stream)); - else - this.source.releaseFD(); - stream.end(); - } - - static [kOnPipedFileHandleRead]() { - const err = streamBaseState[kReadBytesOrError]; - if (err < 0 && err !== UV_EOF) { - this.stream.destroy(errnoException(err, 'sendFD')); - } - } - - get resetReceived() { - const state = this[kInternalState]; - return (state.resetCode !== undefined) ? - { code: state.resetCode | 0 } : - undefined; - } - - get id() { - return this[kInternalState].id; - } - - get push_id() { - return this[kInternalState].push_id; - } - - get session() { - return this[kInternalState].session; - } - - get dataRateHistogram() { - return this[kInternalState].dataRateHistogram; - } - - get dataSizeHistogram() { - return this[kInternalState].dataSizeHistogram; - } - - get dataAckHistogram() { - return this[kInternalState].dataAckHistogram; - } - - pushStream(headers = {}, options = {}) { - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicStream is already destroyed'); - - if (this.detached) { - throw new ERR_INVALID_STATE( - 'Push stream could not be opened on this QuicSession. ' + - 'Push is either disabled or currently blocked.'); - } - - const state = this[kInternalState]; - const { - highWaterMark = state.highWaterMark, - defaultEncoding = state.defaultEncoding, - } = validateQuicStreamOptions(options); - - validateObject(headers, 'headers'); - - // This is a small performance optimization for http/3, - // where push streams are only supported for client - // initiated, bidirectional streams. For any other alpn, - // we'll make the attempt to push and handle the failure - // after. - if (!this.clientInitiated && - !this.bidirectional && - this.session.alpnProtocol === 'h3-29') { - throw new ERR_INVALID_STATE( - 'Push streams are only supported on client-initiated, ' + - 'bidirectional streams'); - } - - // TODO(@jasnell): The assertValidPseudoHeader validator - // here is HTTP/3 specific. If we end up with another - // QUIC application protocol that supports push streams, - // we will need to select a validator based on the - // alpn value. - const handle = this[kHandle].submitPush( - mapToHeaders(headers, assertValidPseudoHeader)); - - // If undefined is returned, it either means that push - // streams are not supported by the underlying application, - // or push streams are currently disabled/blocked. This - // will typically be the case with HTTP/3 when the client - // peer has disabled push. - if (handle === undefined) { - throw new ERR_INVALID_STATE( - 'Push stream could not be opened on this QuicSession. ' + - 'Push is either disabled or currently blocked.'); - } - - return new QuicStream({ - readable: false, - highWaterMark, - defaultEncoding, - }, this.session, handle); - } - - submitInformationalHeaders(headers = {}) { - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicStream is already destroyed'); - - if (this.detached) - throw new ERR_INVALID_STATE('Unable to submit headers'); - - validateObject(headers, 'headers'); - - // TODO(@jasnell): The validators here are specific to the QUIC - // protocol. In the case below, these are the http/3 validators - // (which are identical to the rules for http/2). We need to - // find a way for this to be easily abstracted based on the - // selected alpn. - - let validator; - if (this.session instanceof QuicServerSession) { - validator = - this.clientInitiated ? - assertValidPseudoHeaderResponse : - assertValidPseudoHeader; - } else { // QuicClientSession - validator = - this.clientInitiated ? - assertValidPseudoHeader : - assertValidPseudoHeaderResponse; - } - - return this[kHandle].submitInformationalHeaders( - mapToHeaders(headers, validator)); - } - - submitInitialHeaders(headers = {}, options = {}) { - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicStream is already destroyed'); - - if (this.detached) - throw new ERR_INVALID_STATE('Unable to submit headers'); - - const { terminal } = { ...options }; - - if (terminal !== undefined) - validateBoolean(terminal, 'options.terminal'); - validateObject(headers, 'headers'); - - // TODO(@jasnell): The validators here are specific to the QUIC - // protocol. In the case below, these are the http/3 validators - // (which are identical to the rules for http/2). We need to - // find a way for this to be easily abstracted based on the - // selected alpn. - - let validator; - if (this.session instanceof QuicServerSession) { - validator = - this.clientInitiated ? - assertValidPseudoHeaderResponse : - assertValidPseudoHeader; - } else { // QuicClientSession - validator = - this.clientInitiated ? - assertValidPseudoHeader : - assertValidPseudoHeaderResponse; - } - - return this[kHandle].submitHeaders( - mapToHeaders(headers, validator), - terminal ? - QUICSTREAM_HEADER_FLAGS_TERMINAL : - QUICSTREAM_HEADER_FLAGS_NONE); - } - - submitTrailingHeaders(headers = {}) { - if (this.destroyed) - throw new ERR_INVALID_STATE('QuicStream is already destroyed'); - - if (this.detached) - throw new ERR_INVALID_STATE('Unable to submit headers'); - - validateObject(headers, 'headers'); - - // TODO(@jasnell): The validators here are specific to the QUIC - // protocol. In the case below, these are the http/3 validators - // (which are identical to the rules for http/2). We need to - // find a way for this to be easily abstracted based on the - // selected alpn. - - return this[kHandle].submitTrailers( - mapToHeaders(headers, assertValidPseudoHeaderTrailer)); - } - - get duration() { - const end = getStats(this, IDX_QUIC_STREAM_STATS_DESTROYED_AT) || - process.hrtime.bigint(); - return Number(end - getStats(this, IDX_QUIC_STREAM_STATS_CREATED_AT)); - } - - get bytesReceived() { - return Number(getStats(this, IDX_QUIC_STREAM_STATS_BYTES_RECEIVED)); - } - - get bytesSent() { - return Number(getStats(this, IDX_QUIC_STREAM_STATS_BYTES_SENT)); - } - - get maxExtendedOffset() { - return Number(getStats(this, IDX_QUIC_STREAM_STATS_MAX_OFFSET)); - } - - get finalSize() { - return Number(getStats(this, IDX_QUIC_STREAM_STATS_FINAL_SIZE)); - } - - get maxAcknowledgedOffset() { - return Number(getStats(this, IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK)); - } - - get maxReceivedOffset() { - return Number(getStats(this, IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV)); - } -} - -function createSocket(options) { - return new QuicSocket(options); -} - -module.exports = { - createSocket, - kUDPHandleForTesting, - kRemoveFromSocket, -}; - -/* eslint-enable no-use-before-define */ - -// A single QuicSocket may act as both a Server and a Client. -// There are two kinds of sessions: -// * QuicServerSession -// * QuicClientSession -// -// It is important to understand that QUIC sessions are -// independent of the QuicSocket. A default configuration -// for QuicServerSession and QuicClientSessions may be -// set when the QuicSocket is created, but the actual -// configuration for a particular QuicSession instance is -// not set until the session itself is created. -// -// QuicSockets and QuicSession instances have distinct -// configuration options that apply independently: -// -// QuicSocket Options: -// * `lookup` {Function} A function used to resolve DNS names. -// * `type` {string} Either `'udp4'` or `'udp6'`, defaults to -// `'udp4'`. -// * `port` {number} The local IP port the QuicSocket will -// bind to. -// * `address` {string} The local IP address or hostname that -// the QuicSocket will bind to. If a hostname is given, the -// `lookup` function will be invoked to resolve an IP address. -// * `ipv6Only` -// * `reuseAddr` -// -// Keep in mind that while all QUIC network traffic is encrypted -// using TLS 1.3, every QuicSession maintains it's own SecureContext -// that is completely independent of the QuicSocket. Every -// QuicServerSession and QuicClientSession could, in theory, -// use a completely different TLS 1.3 configuration. To keep it -// simple, however, we use the same SecureContext for all QuicServerSession -// instances, but that may be something we want to revisit later. -// -// Every QuicSession has two sets of configuration parameters: -// * Options -// * Transport Parameters -// -// Options establish implementation specific operation parameters, -// such as the default highwatermark for new QuicStreams. Transport -// Parameters are QUIC specific and are passed to the peer as part -// of the TLS handshake. -// -// Every QuicSession may have separate options and transport -// parameters, even within the same QuicSocket, so the configuration -// must be established when the session is created. -// -// When creating a QuicSocket, it is possible to set a default -// configuration for both QuicServerSession and QuicClientSession -// options. -// -// const soc = createSocket({ -// type: 'udp4', -// port: 0, -// server: { -// // QuicServerSession configuration defaults -// }, -// client: { -// // QuicClientSession configuration defaults -// } -// }); -// -// When calling listen() on the created QuicSocket, the server -// specific configuration that will be used for all new -// QuicServerSession instances will be given, with the values -// provided to createSocket() using the server option used -// as a default. -// -// When calling connect(), the client specific configuration -// will be given, with the values provided to the createSocket() -// using the client option used as a default. - - -// Some lifecycle documentation for the various objects: -// -// QuicSocket -// Close -// * Close all existing Sessions -// * Do not allow any new Sessions (inbound or outbound) -// * Destroy once there are no more sessions - -// Destroy -// * Destroy all remaining sessions -// * Destroy and free the QuicSocket handle immediately -// * If Error, emit Error event -// * Emit Close event - -// QuicClientSession -// Close -// * Allow existing Streams to complete normally -// * Do not allow any new Streams (inbound or outbound) -// * Destroy once there are no more streams - -// Destroy -// * Send CONNECTION_CLOSE -// * Destroy all remaining Streams -// * Remove Session from Parent Socket -// * Destroy and free the QuicSession handle immediately -// * If Error, emit Error event -// * Emit Close event - -// QuicServerSession -// Close -// * Allow existing Streams to complete normally -// * Do not allow any new Streams (inbound or outbound) -// * Destroy once there are no more streams -// Destroy -// * Send CONNECTION_CLOSE -// * Destroy all remaining Streams -// * Remove Session from Parent Socket -// * Destroy and free the QuicSession handle immediately -// * If Error, emit Error event -// * Emit Close event - -// QuicStream -// Destroy -// * Remove Stream From Parent Session -// * Destroy and free the QuicStream handle immediately -// * If Error, emit Error event -// * Emit Close event - -// QuicEndpoint States: -// Initial -- Created, Endpoint not yet bound to local UDP -// port. -// Pending -- Binding to local UDP port has been initialized. -// Bound -- Binding to local UDP port has completed. -// Closed -- Endpoint has been unbound from the local UDP -// port. -// Error -- An error has been encountered, endpoint has -// been unbound and is no longer usable. -// -// QuicSocket States: -// Initial -- Created, QuicSocket has at least one -// QuicEndpoint. All QuicEndpoints are in the -// Initial state. -// Pending -- QuicSocket has at least one QuicEndpoint in the -// Pending state. -// Bound -- QuicSocket has at least one QuicEndpoint in the -// Bound state. -// Closed -- All QuicEndpoints are in the closed state. -// Destroyed -- QuicSocket is no longer usable -// Destroyed-With-Error -- An error has been encountered, socket is no -// longer usable -// -// QuicSession States: -// Initial -- Created, QuicSocket state undetermined, -// no internal QuicSession Handle assigned. -// Ready -// QuicSocket Ready -- QuicSocket in Bound state. -// Handle Assigned -- Internal QuicSession Handle assigned. -// TLS Handshake Started -- -// TLS Handshake Completed -- -// TLS Handshake Confirmed -- -// Closed -- Graceful Close Initiated -// Destroyed -- QuicSession is no longer usable -// Destroyed-With-Error -- An error has been encountered, session is no -// longer usable -// -// QuicStream States: -// Initial Writable/Corked -- Created, QuicSession in Initial State -// Open Writable/Uncorked -- QuicSession in Ready State -// Closed -- Graceful Close Initiated -// Destroyed -- QuicStream is no longer usable -// Destroyed-With-Error -- An error has been encountered, stream is no -// longer usable diff --git a/lib/internal/quic/util.js b/lib/internal/quic/util.js deleted file mode 100644 index 97da6953647..00000000000 --- a/lib/internal/quic/util.js +++ /dev/null @@ -1,1052 +0,0 @@ -'use strict'; - -const { - Boolean, - Number, - NumberINFINITY, - NumberNEGATIVE_INFINITY, -} = primordials; - -const { - codes: { - ERR_INVALID_ARG_TYPE, - ERR_INVALID_ARG_VALUE, - ERR_OUT_OF_RANGE, - }, -} = require('internal/errors'); - -const { - symbols: { - owner_symbol, - }, -} = require('internal/async_hooks'); - -const { inspect } = require('internal/util/inspect'); - -const { Readable } = require('stream'); -const { - kHandle, - kUpdateTimer, - onStreamRead, -} = require('internal/stream_base_commons'); - -const endianness = require('os').endianness(); - -const assert = require('internal/assert'); -assert(process.versions.ngtcp2 !== undefined); - -const { - isIP, -} = require('internal/net'); - -const { - getOptionValue, - getAllowUnauthorized, -} = require('internal/options'); - -const { Buffer } = require('buffer'); - -const { - sessionConfig, - http3Config, - constants: { - AF_INET, - AF_INET6, - NGHTTP3_ALPN_H3, - DEFAULT_RETRYTOKEN_EXPIRATION, - DEFAULT_MAX_CONNECTIONS, - DEFAULT_MAX_CONNECTIONS_PER_HOST, - DEFAULT_MAX_STATELESS_RESETS_PER_HOST, - IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT, - IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL, - IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE, - IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI, - IDX_QUIC_SESSION_MAX_DATA, - IDX_QUIC_SESSION_MAX_STREAMS_BIDI, - IDX_QUIC_SESSION_MAX_STREAMS_UNI, - IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT, - IDX_QUIC_SESSION_MAX_ACK_DELAY, - IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE, - IDX_QUIC_SESSION_CC_ALGO, - IDX_QUIC_SESSION_CONFIG_COUNT, - - IDX_QUICSESSION_STATE_KEYLOG_ENABLED, - IDX_QUICSESSION_STATE_CLIENT_HELLO_ENABLED, - IDX_QUICSESSION_STATE_OCSP_ENABLED, - IDX_QUICSESSION_STATE_PATH_VALIDATED_ENABLED, - IDX_QUICSESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, - IDX_QUICSESSION_STATE_HANDSHAKE_CONFIRMED, - IDX_QUICSESSION_STATE_IDLE_TIMEOUT, - IDX_QUICSESSION_STATE_MAX_STREAMS_BIDI, - IDX_QUICSESSION_STATE_MAX_STREAMS_UNI, - IDX_QUICSESSION_STATE_MAX_DATA_LEFT, - IDX_QUICSESSION_STATE_BYTES_IN_FLIGHT, - - IDX_QUICSOCKET_STATE_SERVER_LISTENING, - IDX_QUICSOCKET_STATE_SERVER_BUSY, - IDX_QUICSOCKET_STATE_STATELESS_RESET_DISABLED, - - IDX_QUICSTREAM_STATE_WRITE_ENDED, - IDX_QUICSTREAM_STATE_READ_STARTED, - IDX_QUICSTREAM_STATE_READ_PAUSED, - IDX_QUICSTREAM_STATE_READ_ENDED, - IDX_QUICSTREAM_STATE_FIN_SENT, - IDX_QUICSTREAM_STATE_FIN_RECEIVED, - - IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, - IDX_HTTP3_QPACK_BLOCKED_STREAMS, - IDX_HTTP3_MAX_HEADER_LIST_SIZE, - IDX_HTTP3_MAX_PUSHES, - IDX_HTTP3_MAX_HEADER_PAIRS, - IDX_HTTP3_MAX_HEADER_LENGTH, - IDX_HTTP3_CONFIG_COUNT, - MAX_RETRYTOKEN_EXPIRATION, - MIN_RETRYTOKEN_EXPIRATION, - NGTCP2_NO_ERROR, - NGTCP2_MAX_CIDLEN, - NGTCP2_MIN_CIDLEN, - NGTCP2_CC_ALGO_CUBIC, - NGTCP2_CC_ALGO_RENO, - QUIC_PREFERRED_ADDRESS_IGNORE, - QUIC_PREFERRED_ADDRESS_USE, - QUIC_ERROR_APPLICATION, - } -} = internalBinding('quic'); - -const { - validateBoolean, - validateBuffer, - validateFunction, - validateInteger, - validateObject, - validatePort, - validateString, -} = require('internal/validators'); - -const kDefaultQuicCiphers = 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:' + - 'TLS_CHACHA20_POLY1305_SHA256'; -const kDefaultGroups = 'P-256:X25519:P-384:P-521'; - -let dns; - -function lazyDNS() { - if (!dns) - dns = require('dns').promises; - return dns; -} - -// This is here rather than in internal/validators to -// prevent performance degredation in default cases. -function validateNumber(value, name, - min = NumberNEGATIVE_INFINITY, - max = NumberINFINITY) { - if (typeof value !== 'number') - throw new ERR_INVALID_ARG_TYPE(name, 'number', value); - if (value < min || value > max) - throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); -} - -function getSocketType(type = 'udp4') { - switch (type) { - case 'udp4': return [AF_INET, false]; - case 'udp6': return [AF_INET6, false]; - case 'udp6-only': return [AF_INET6, true]; - } - throw new ERR_INVALID_ARG_VALUE('options.type', type); -} - -function defaultLookup(address, type) { - const { lookup } = lazyDNS(); - return lookup(address || (type === 6 ? '::1' : '127.0.0.1'), type); -} - -function validateCloseCode(code) { - if (code != null && typeof code === 'object') { - return { - closeCode: code.code || NGTCP2_NO_ERROR, - closeFamily: code.family || QUIC_ERROR_APPLICATION, - }; - } else if (typeof code === 'number') { - return { - closeCode: code, - closeFamily: QUIC_ERROR_APPLICATION, - }; - } - throw new ERR_INVALID_ARG_TYPE('code', ['number', 'Object'], code); -} - -function validateLookup(lookup) { - if (lookup) { - validateFunction(lookup, 'options.lookup'); - } -} - -function validatePreferredAddress(address) { - if (address !== undefined) { - validateObject(address, 'options.preferredAddress'); - validateString(address.address, 'options.preferredAddress.address'); - if (address.port !== undefined) - validatePort(address.port, 'options.preferredAddress.port'); - getSocketType(address.type); - } - return address; -} - -// Validate known transport parameters, ignoring any that are not -// supported. Ensures that only supported parameters are passed on. -function validateTransportParams(params) { - const { - activeConnectionIdLimit, - maxStreamDataBidiLocal, - maxStreamDataBidiRemote, - maxStreamDataUni, - maxData, - maxStreamsBidi, - maxStreamsUni, - idleTimeout, - maxUdpPayloadSize, - maxAckDelay, - preferredAddress, - rejectUnauthorized, - requestCert, - congestionAlgorithm = 'reno', - h3: { - qpackMaxTableCapacity, - qpackBlockedStreams, - maxHeaderListSize, - maxPushes, - maxHeaderPairs, - maxHeaderLength = getOptionValue('--max-http-header-size'), - }, - } = { h3: {}, ...params }; - - if (activeConnectionIdLimit !== undefined) { - validateInteger( - activeConnectionIdLimit, - 'options.activeConnectionIdLimit', - /* min */ 2, - /* max */ 8); - } - if (maxStreamDataBidiLocal !== undefined) { - validateInteger( - maxStreamDataBidiLocal, - 'options.maxStreamDataBidiLocal', - /* min */ 0); - } - if (maxStreamDataBidiRemote !== undefined) { - validateInteger( - maxStreamDataBidiRemote, - 'options.maxStreamDataBidiRemote', - /* min */ 0); - } - if (maxStreamDataUni !== undefined) { - validateInteger( - maxStreamDataUni, - 'options.maxStreamDataUni', - /* min */ 0); - } - if (maxData !== undefined) { - validateInteger( - maxData, - 'options.maxData', - /* min */ 0); - } - if (maxStreamsBidi !== undefined) { - validateInteger( - maxStreamsBidi, - 'options.maxStreamsBidi', - /* min */ 0); - } - if (maxStreamsUni !== undefined) { - validateInteger( - maxStreamsUni, - 'options.maxStreamsUni', - /* min */ 0); - } - if (idleTimeout !== undefined) { - validateInteger( - idleTimeout, - 'options.idleTimeout', - /* min */ 0); - } - if (maxUdpPayloadSize !== undefined) { - validateInteger( - maxUdpPayloadSize, - 'options.maxUdpPayloadSize', - /* min */ 0); - } - if (maxAckDelay !== undefined) { - validateInteger( - maxAckDelay, - 'options.maxAckDelay', - /* min */ 0); - } - if (qpackMaxTableCapacity !== undefined) { - validateInteger( - qpackMaxTableCapacity, - 'options.h3.qpackMaxTableCapacity', - /* min */ 0); - } - if (qpackBlockedStreams !== undefined) { - validateInteger( - qpackBlockedStreams, - 'options.h3.qpackBlockedStreams', - /* min */ 0); - } - if (maxHeaderListSize !== undefined) { - validateInteger( - maxHeaderListSize, - 'options.h3.maxHeaderListSize', - /* min */ 0); - } - if (maxPushes !== undefined) { - validateInteger( - maxPushes, - 'options.h3.maxPushes', - /* min */ 0); - } - if (maxHeaderPairs !== undefined) { - validateInteger( - maxHeaderPairs, - 'options.h3.maxHeaderPairs', - /* min */ 0); - } - if (maxHeaderLength !== undefined) { - validateInteger( - maxHeaderLength, - 'options.h3.maxHeaderLength', - /* min */ 0); - } - let ccAlgo = NGTCP2_CC_ALGO_RENO; - if (congestionAlgorithm !== undefined) { - validateString(congestionAlgorithm, 'options.conjestionAlgorithm'); - switch (congestionAlgorithm) { - case 'reno': - // This is the default - break; - case 'cubic': - ccAlgo = NGTCP2_CC_ALGO_CUBIC; - break; - default: - throw new ERR_INVALID_ARG_VALUE( - 'options.congestionAlgorithm', - congestionAlgorithm, - 'is not supported'); - } - } - - validatePreferredAddress(preferredAddress); - - return { - activeConnectionIdLimit, - maxStreamDataBidiLocal, - maxStreamDataBidiRemote, - maxStreamDataUni, - maxData, - maxStreamsBidi, - maxStreamsUni, - idleTimeout, - maxUdpPayloadSize, - maxAckDelay, - preferredAddress, - rejectUnauthorized, - requestCert, - congestionAlgorithm: ccAlgo, - h3: { - qpackMaxTableCapacity, - qpackBlockedStreams, - maxHeaderListSize, - maxPushes, - maxHeaderPairs, - maxHeaderLength, - } - }; -} - -function validateQuicClientSessionOptions(options = {}) { - const { - autoStart = true, - address = 'localhost', - alpn = '', - dcid: dcid_value, - minDHSize = 1024, - ocspHandler, - port = 0, - preferredAddressPolicy = 'ignore', - remoteTransportParams, - servername = (isIP(address) ? '' : address), - sessionTicket, - verifyHostnameIdentity = true, - qlog = false, - highWaterMark, - defaultEncoding, - } = options; - - validateBoolean(autoStart, 'options.autoStart'); - validateNumber(minDHSize, 'options.minDHSize'); - validatePort(port, 'options.port'); - validateString(address, 'options.address'); - validateString(alpn, 'options.alpn'); - validateString(servername, 'options.servername'); - - if (isIP(servername)) { - throw new ERR_INVALID_ARG_VALUE( - 'options.servername', - servername, - 'cannot be an IP address'); - } - - if (remoteTransportParams !== undefined) - validateBuffer(remoteTransportParams, 'options.remoteTransportParams'); - - if (sessionTicket !== undefined) - validateBuffer(sessionTicket, 'options.sessionTicket'); - - let dcid; - if (dcid_value !== undefined) { - if (typeof dcid_value === 'string') { - // If it's a string, it must be a hex encoded string - try { - Buffer.from(dcid_value, 'hex'); - } catch { - throw new ERR_INVALID_ARG_VALUE( - 'options.dcid', - dcid_value, - 'is not a valid QUIC connection ID'); - } - } - - validateBuffer( - dcid_value, - 'options.dcid', - ['string', 'Buffer', 'TypedArray', 'DataView']); - - if (dcid_value.length > NGTCP2_MAX_CIDLEN || - dcid_value.length < NGTCP2_MIN_CIDLEN) { - throw new ERR_INVALID_ARG_VALUE( - 'options.dcid', - dcid_value.toString('hex'), - 'is not a valid QUIC connection ID' - ); - } - - dcid = dcid_value; - } - - if (preferredAddressPolicy !== undefined) - validateString(preferredAddressPolicy, 'options.preferredAddressPolicy'); - - validateBoolean(verifyHostnameIdentity, 'options.verifyHostnameIdentity'); - validateBoolean(qlog, 'options.qlog'); - - if (ocspHandler !== undefined && typeof ocspHandler !== 'function') { - throw new ERR_INVALID_ARG_TYPE( - 'options.ocspHandler', - 'functon', - ocspHandler); - } - - return { - autoStart, - address, - alpn, - dcid, - minDHSize, - ocspHandler, - port, - preferredAddressPolicy: - preferredAddressPolicy === 'accept' ? - QUIC_PREFERRED_ADDRESS_USE : - QUIC_PREFERRED_ADDRESS_IGNORE, - remoteTransportParams, - servername, - sessionTicket, - verifyHostnameIdentity, - qlog, - ...validateQuicStreamOptions({ highWaterMark, defaultEncoding }) - }; -} - -function validateQuicStreamOptions(options = {}) { - validateObject(options); - const { - defaultEncoding = 'utf8', - halfOpen, - highWaterMark, - } = options; - if (!Buffer.isEncoding(defaultEncoding)) { - throw new ERR_INVALID_ARG_VALUE( - 'options.defaultEncoding', - defaultEncoding, - 'is not a valid encoding'); - } - if (halfOpen !== undefined) - validateBoolean(halfOpen, 'options.halfOpen'); - if (highWaterMark !== undefined) { - validateInteger( - highWaterMark, - 'options.highWaterMark', - /* min */ 0); - } - return { - defaultEncoding, - halfOpen, - highWaterMark, - }; -} - -function validateQuicEndpointOptions(options = {}, name = 'options') { - validateObject(options, name); - const { - address, - lookup, - port = 0, - reuseAddr = false, - type = 'udp4', - preferred = false, - } = options; - if (address !== undefined) - validateString(address, 'options.address'); - validatePort(port, 'options.port'); - validateString(type, 'options.type'); - validateLookup(lookup); - validateBoolean(reuseAddr, 'options.reuseAddr'); - validateBoolean(preferred, 'options.preferred'); - const [typeVal, ipv6Only] = getSocketType(type); - return { - type: typeVal, - ipv6Only, - address, - lookup, - port, - preferred, - reuseAddr, - }; -} - -function validateQuicSocketOptions(options = {}) { - validateObject(options, 'options'); - - const { - client = {}, - disableStatelessReset = false, - endpoint = { port: 0, type: 'udp4' }, - lookup = defaultLookup, - maxConnections = DEFAULT_MAX_CONNECTIONS, - maxConnectionsPerHost = DEFAULT_MAX_CONNECTIONS_PER_HOST, - maxStatelessResetsPerHost = DEFAULT_MAX_STATELESS_RESETS_PER_HOST, - qlog = false, - retryTokenTimeout = DEFAULT_RETRYTOKEN_EXPIRATION, - server = {}, - statelessResetSecret, - validateAddress = false, - } = options; - - const { type } = validateQuicEndpointOptions(endpoint, 'options.endpoint'); - validateObject(client, 'options.client'); - validateObject(server, 'options.server'); - validateLookup(lookup); - validateBoolean(validateAddress, 'options.validateAddress'); - validateBoolean(qlog, 'options.qlog'); - validateBoolean(disableStatelessReset, 'options.disableStatelessReset'); - - if (retryTokenTimeout !== undefined) { - validateInteger( - retryTokenTimeout, - 'options.retryTokenTimeout', - /* min */ MIN_RETRYTOKEN_EXPIRATION, - /* max */ MAX_RETRYTOKEN_EXPIRATION); - } - if (maxConnections !== undefined) { - validateInteger( - maxConnections, - 'options.maxConnections', - /* min */ 1); - } - if (maxConnectionsPerHost !== undefined) { - validateInteger( - maxConnectionsPerHost, - 'options.maxConnectionsPerHost', - /* min */ 1); - } - if (maxStatelessResetsPerHost !== undefined) { - validateInteger( - maxStatelessResetsPerHost, - 'options.maxStatelessResetsPerHost', - /* min */ 1); - } - - if (statelessResetSecret !== undefined) { - validateBuffer(statelessResetSecret, 'options.statelessResetSecret'); - if (statelessResetSecret.length !== 16) - throw new ERR_INVALID_ARG_VALUE( - 'options.statelessResetSecret', - statelessResetSecret, - 'must be exactly 16 bytes in length'); - } - - return { - endpoint, - client, - lookup, - maxConnections, - maxConnectionsPerHost, - maxStatelessResetsPerHost, - retryTokenTimeout, - server, - type, - validateAddress, - qlog, - statelessResetSecret, - disableStatelessReset, - }; -} - -function validateQuicSocketListenOptions(options = {}) { - validateObject(options); - const { - alpn = NGHTTP3_ALPN_H3, - defaultEncoding, - highWaterMark, - ocspHandler, - clientHelloHandler, - requestCert, - rejectUnauthorized, - lookup, - } = options; - validateString(alpn, 'options.alpn'); - if (rejectUnauthorized !== undefined) - validateBoolean(rejectUnauthorized, 'options.rejectUnauthorized'); - if (requestCert !== undefined) - validateBoolean(requestCert, 'options.requestCert'); - if (lookup !== undefined) - validateLookup(lookup); - if (ocspHandler !== undefined && typeof ocspHandler !== 'function') { - throw new ERR_INVALID_ARG_TYPE( - 'options.ocspHandler', - 'function', - ocspHandler); - } - if (clientHelloHandler !== undefined && - typeof clientHelloHandler !== 'function') { - throw new ERR_INVALID_ARG_TYPE( - 'options.clientHelloHandler', - 'function', - clientHelloHandler); - } - const transportParams = validateTransportParams(options); - - return { - alpn, - lookup, - ocspHandler, - clientHelloHandler, - rejectUnauthorized, - requestCert, - transportParams, - ...validateQuicStreamOptions({ highWaterMark, defaultEncoding }) - }; -} - -function validateQuicSocketConnectOptions(options = {}) { - validateObject(options); - const { - type = 'udp4', - address, - lookup, - } = options; - if (address !== undefined) - validateString(address, 'options.address'); - if (lookup !== undefined) - validateLookup(lookup); - const [typeVal] = getSocketType(type); - return { type: typeVal, address, lookup }; -} - -function validateCreateSecureContextOptions(options = {}) { - validateObject(options); - const { - ca, - cert, - ciphers = kDefaultQuicCiphers, - clientCertEngine, - crl, - dhparam, - ecdhCurve, - groups = kDefaultGroups, - honorCipherOrder, - key, - earlyData = true, // Early data is enabled by default - passphrase, - pfx, - sessionIdContext, - secureProtocol - } = options; - validateString(ciphers, 'option.ciphers'); - validateString(groups, 'option.groups'); - if (earlyData !== undefined) - validateBoolean(earlyData, 'option.earlyData'); - - // Additional validation occurs within the tls - // createSecureContext function. - return { - ca, - cert, - ciphers, - clientCertEngine, - crl, - dhparam, - ecdhCurve, - groups, - honorCipherOrder, - key, - earlyData, - passphrase, - pfx, - sessionIdContext, - secureProtocol - }; -} - -function setConfigField(buffer, val, index) { - if (typeof val === 'number') { - buffer[index] = val; - return 1 << index; - } - return 0; -} - -// Extracts configuration options and updates the aliased buffer -// arrays that are used to communicate config choices to the c++ -// internals. -function setTransportParams(config) { - const { - activeConnectionIdLimit, - maxStreamDataBidiLocal, - maxStreamDataBidiRemote, - maxStreamDataUni, - maxData, - maxStreamsBidi, - maxStreamsUni, - idleTimeout, - maxUdpPayloadSize, - maxAckDelay, - congestionAlgorithm, - h3: { - qpackMaxTableCapacity, - qpackBlockedStreams, - maxHeaderListSize, - maxPushes, - maxHeaderPairs, - maxHeaderLength, - }, - } = { h3: {}, ...config }; - - // The const flags is a bitmap that is used to communicate whether or not a - // given configuration value has been explicitly provided. - const flags = setConfigField(sessionConfig, - activeConnectionIdLimit, - IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT) | - setConfigField(sessionConfig, - maxStreamDataBidiLocal, - IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL) | - setConfigField(sessionConfig, - maxStreamDataBidiRemote, - IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE) | - setConfigField(sessionConfig, - maxStreamDataUni, - IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI) | - setConfigField(sessionConfig, - maxData, - IDX_QUIC_SESSION_MAX_DATA) | - setConfigField(sessionConfig, - maxStreamsBidi, - IDX_QUIC_SESSION_MAX_STREAMS_BIDI) | - setConfigField(sessionConfig, - maxStreamsUni, - IDX_QUIC_SESSION_MAX_STREAMS_UNI) | - setConfigField(sessionConfig, - idleTimeout, - IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) | - setConfigField(sessionConfig, - maxAckDelay, - IDX_QUIC_SESSION_MAX_ACK_DELAY) | - setConfigField(sessionConfig, - maxUdpPayloadSize, - IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE) | - setConfigField(sessionConfig, - congestionAlgorithm, - IDX_QUIC_SESSION_CC_ALGO); - - sessionConfig[IDX_QUIC_SESSION_CONFIG_COUNT] = flags; - - const h3flags = setConfigField(http3Config, - qpackMaxTableCapacity, - IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY) | - setConfigField(http3Config, - qpackBlockedStreams, - IDX_HTTP3_QPACK_BLOCKED_STREAMS) | - setConfigField(http3Config, - maxHeaderListSize, - IDX_HTTP3_MAX_HEADER_LIST_SIZE) | - setConfigField(http3Config, - maxPushes, - IDX_HTTP3_MAX_PUSHES) | - setConfigField(http3Config, - maxHeaderPairs, - IDX_HTTP3_MAX_HEADER_PAIRS) | - setConfigField(http3Config, - maxHeaderLength, - IDX_HTTP3_MAX_HEADER_LENGTH); - - http3Config[IDX_HTTP3_CONFIG_COUNT] = h3flags; -} - -// Some events that are emitted originate from the C++ internals and are -// fairly expensive and optional. An aliased array buffer is used to -// communicate that a handler has been added for the optional events -// so that the C++ internals know there is an actual listener. The event -// will not be emitted if there is no handler. -function toggleListeners(state, event, on) { - if (state === undefined) - return; - switch (event) { - case 'keylog': - state.keylogEnabled = on; - break; - case 'pathValidation': - state.pathValidatedEnabled = on; - break; - case 'usePreferredAddress': - state.usePreferredAddressEnabled = on; - break; - } -} - -class QuicStreamSharedState { - constructor(state) { - this[kHandle] = Buffer.from(state); - } - - get writeEnded() { - return Boolean(this[kHandle].readUInt8(IDX_QUICSTREAM_STATE_WRITE_ENDED)); - } - - set writeEnded(on) { - this[kHandle].writeUInt8(on ? 1 : 0, IDX_QUICSTREAM_STATE_WRITE_ENDED); - } - - get readStarted() { - return Boolean(this[kHandle].readUInt8(IDX_QUICSTREAM_STATE_READ_STARTED)); - } - - get readPaused() { - return Boolean(this[kHandle].readUInt8(IDX_QUICSTREAM_STATE_READ_PAUSED)); - } - - set readPaused(on) { - this[kHandle].writeUInt8(on ? 1 : 0, IDX_QUICSTREAM_STATE_READ_PAUSED); - } - - get readEnded() { - return Boolean(this[kHandle].readUInt8(IDX_QUICSTREAM_STATE_READ_ENDED)); - } - - set readEnded(on) { - this[kHandle].writeUInt8(on ? 1 : 0, IDX_QUICSTREAM_STATE_READ_ENDED); - } - - get finSent() { - return Boolean(this[kHandle].readUInt8(IDX_QUICSTREAM_STATE_FIN_SENT)); - } - - get finReceived() { - return Boolean(this[kHandle].readUInt8(IDX_QUICSTREAM_STATE_FIN_RECEIVED)); - } -} - -class QuicSocketSharedState { - constructor(state) { - this[kHandle] = Buffer.from(state); - } - - get serverListening() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSOCKET_STATE_SERVER_LISTENING)); - } - - set serverListening(on) { - this[kHandle].writeUInt8(on ? 1 : 0, IDX_QUICSOCKET_STATE_SERVER_LISTENING); - } - - get serverBusy() { - return Boolean(this[kHandle].readUInt8(IDX_QUICSOCKET_STATE_SERVER_BUSY)); - } - - set serverBusy(on) { - this[kHandle].writeUInt8(on ? 1 : 0, IDX_QUICSOCKET_STATE_SERVER_BUSY); - } - - get statelessResetDisabled() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSOCKET_STATE_STATELESS_RESET_DISABLED)); - } - - set statelessResetDisabled(on) { - this[kHandle].writeUInt8(on ? 1 : 0, - IDX_QUICSOCKET_STATE_STATELESS_RESET_DISABLED); - } -} - -// A utility class used to handle reading / modifying shared JS/C++ -// state associated with a QuicSession -class QuicSessionSharedState { - constructor(state) { - this[kHandle] = Buffer.from(state); - } - - get keylogEnabled() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSESSION_STATE_KEYLOG_ENABLED)); - } - - set keylogEnabled(on) { - this[kHandle] - .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_KEYLOG_ENABLED); - } - - get clientHelloEnabled() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSESSION_STATE_CLIENT_HELLO_ENABLED)); - } - - set clientHelloEnabled(on) { - this[kHandle] - .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_CLIENT_HELLO_ENABLED); - } - - get ocspEnabled() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSESSION_STATE_OCSP_ENABLED)); - } - - set ocspEnabled(on) { - this[kHandle] - .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_OCSP_ENABLED); - } - - get pathValidatedEnabled() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSESSION_STATE_PATH_VALIDATED_ENABLED)); - } - - set pathValidatedEnabled(on) { - this[kHandle] - .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_PATH_VALIDATED_ENABLED); - } - - get usePreferredAddressEnabled() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED)); - } - - set usePreferredAddressEnabled(on) { - this[kHandle] - .writeUInt8(on ? 1 : 0, - IDX_QUICSESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED); - } - - get handshakeConfirmed() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSESSION_STATE_HANDSHAKE_CONFIRMED)); - } - - get idleTimeout() { - return Boolean(this[kHandle] - .readUInt8(IDX_QUICSESSION_STATE_IDLE_TIMEOUT)); - } - - get maxStreamsBidi() { - return Number(endianness === 'BE' ? - this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_MAX_STREAMS_BIDI) : - this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_MAX_STREAMS_BIDI)); - } - - get maxStreamsUni() { - return Number(endianness === 'BE' ? - this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_MAX_STREAMS_UNI) : - this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_MAX_STREAMS_UNI)); - } - - get maxDataLeft() { - return Number(endianness === 'BE' ? - this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_MAX_DATA_LEFT) : - this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_MAX_DATA_LEFT)); - } - - get bytesInFlight() { - return Number(endianness === 'BE' ? - this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_BYTES_IN_FLIGHT) : - this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_BYTES_IN_FLIGHT)); - } -} - -class QLogStream extends Readable { - constructor(handle) { - super({ autoDestroy: true }); - this[kHandle] = handle; - handle[owner_symbol] = this; - handle.onread = onStreamRead; - } - - _read() { - if (this[kHandle]) - this[kHandle].readStart(); - } - - _destroy() { - // Release the references on the handle so that - // it can be garbage collected. - this[kHandle][owner_symbol] = undefined; - this[kHandle] = undefined; - } - - [kUpdateTimer]() { - // Does nothing - } -} - -function customInspect(self, obj, depth, options) { - if (depth < 0) - return self; - - const opts = { - ...options, - depth: options.depth == null ? null : options.depth - 1 - }; - - return `${self.constructor.name} ${inspect(obj, opts)}`; -} - -module.exports = { - customInspect, - getAllowUnauthorized, - getSocketType, - defaultLookup, - setTransportParams, - toggleListeners, - validateNumber, - validateCloseCode, - validateTransportParams, - validateQuicClientSessionOptions, - validateQuicSocketOptions, - validateQuicStreamOptions, - validateQuicSocketListenOptions, - validateQuicEndpointOptions, - validateCreateSecureContextOptions, - validateQuicSocketConnectOptions, - QuicStreamSharedState, - QuicSocketSharedState, - QuicSessionSharedState, - QLogStream, -}; diff --git a/lib/net.js b/lib/net.js index fcc911cb865..94aae1fa9f7 100644 --- a/lib/net.js +++ b/lib/net.js @@ -45,9 +45,6 @@ let debug = require('internal/util/debuglog').debuglog('net', (fn) => { debug = fn; }); const { - assertCrypto, -} = require('internal/util'); -const { isIP, isIPv4, isIPv6, @@ -1777,25 +1774,3 @@ module.exports = { Socket, Stream: Socket, // Legacy naming }; - -if (process.versions.ngtcp2 !== undefined) { - let quic; - - function lazyQuic() { - if (quic === undefined) { - assertCrypto(); - quic = require('internal/quic/core'); - process.emitWarning( - 'The QUIC protocol is experimental and not yet ' + - 'supported for production use', - 'ExperimentalWarning'); - } - return quic; - } - - function createQuicSocket(...args) { - return lazyQuic().createSocket(...args); - } - - module.exports.createQuicSocket = createQuicSocket; -} @@ -1,6 +1,5 @@ { 'variables': { - 'experimental_quic': 'false', 'v8_use_siphash%': 0, 'v8_trace_maps%': 0, 'v8_enable_pointer_compression%': 0, @@ -21,8 +20,6 @@ 'node_shared_cares%': 'false', 'node_shared_libuv%': 'false', 'node_shared_nghttp2%': 'false', - 'node_shared_ngtcp2%': 'false', - 'node_shared_nghttp3%': 'false', 'node_use_openssl%': 'true', 'node_shared_openssl%': 'false', 'node_v8_options%': '', @@ -211,8 +208,6 @@ 'lib/internal/process/task_queues.js', 'lib/internal/querystring.js', 'lib/internal/readline/utils.js', - 'lib/internal/quic/core.js', - 'lib/internal/quic/util.js', 'lib/internal/repl.js', 'lib/internal/repl/await.js', 'lib/internal/repl/history.js', @@ -980,38 +975,6 @@ 'node_target_type=="executable"', { 'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ], }], - [ - # We can only use QUIC if using our modified, static linked - # OpenSSL because we have patched in the QUIC support. - 'node_use_openssl=="true" and node_shared_openssl=="false" and experimental_quic==1', { - 'defines': ['NODE_EXPERIMENTAL_QUIC=1'], - 'sources': [ - 'src/node_bob.h', - 'src/node_bob-inl.h', - 'src/quic/node_quic_buffer.h', - 'src/quic/node_quic_buffer-inl.h', - 'src/quic/node_quic_crypto.h', - 'src/quic/node_quic_session.h', - 'src/quic/node_quic_session-inl.h', - 'src/quic/node_quic_socket.h', - 'src/quic/node_quic_socket-inl.h', - 'src/quic/node_quic_stream.h', - 'src/quic/node_quic_stream-inl.h', - 'src/quic/node_quic_util.h', - 'src/quic/node_quic_util-inl.h', - 'src/quic/node_quic_state.h', - 'src/quic/node_quic_default_application.h', - 'src/quic/node_quic_http3_application.h', - 'src/quic/node_quic_buffer.cc', - 'src/quic/node_quic_crypto.cc', - 'src/quic/node_quic_session.cc', - 'src/quic/node_quic_socket.cc', - 'src/quic/node_quic_stream.cc', - 'src/quic/node_quic.cc', - 'src/quic/node_quic_default_application.cc', - 'src/quic/node_quic_http3_application.cc' - ] - }], [ 'use_openssl_def==1', { # TODO(bnoordhuis) Make all platforms export the same list of symbols. # Teach mkssldef.py to generate linker maps that UNIX linkers understand. @@ -1375,15 +1338,6 @@ 'test/cctest/test_node_crypto.cc', ] }], - [ 'node_use_openssl=="true" and experimental_quic==1', { - 'defines': [ - 'NODE_EXPERIMENTAL_QUIC=1', - ], - 'sources': [ - 'test/cctest/test_quic_buffer.cc', - 'test/cctest/test_quic_cid.cc' - ] - }], ['v8_enable_inspector==1', { 'sources': [ 'test/cctest/test_inspector_socket.cc', @@ -1539,11 +1493,6 @@ 'HAVE_OPENSSL=1', ], }], - [ 'node_use_openssl=="true" and experimental_quic==1', { - 'defines': [ - 'NODE_EXPERIMENTAL_QUIC=1', - ], - }], ['v8_enable_inspector==1', { 'defines': [ 'HAVE_INSPECTOR=1', @@ -1598,11 +1547,6 @@ 'HAVE_OPENSSL=1', ], }], - [ 'node_use_openssl=="true" and experimental_quic==1', { - 'defines': [ - 'NODE_EXPERIMENTAL_QUIC=1', - ], - }], ['v8_enable_inspector==1', { 'defines': [ 'HAVE_INSPECTOR=1', diff --git a/node.gypi b/node.gypi index dbe1b05cf54..43dbda7bbf5 100644 --- a/node.gypi +++ b/node.gypi @@ -187,24 +187,6 @@ 'dependencies': [ 'deps/nghttp2/nghttp2.gyp:nghttp2' ], }], - [ - 'experimental_quic==1', { - 'conditions': [ - [ - 'node_shared_ngtcp2=="false"', { - 'dependencies': [ - 'deps/ngtcp2/ngtcp2.gyp:ngtcp2', - ]} - ], - [ - 'node_shared_nghttp3=="false"', { - 'dependencies': [ - 'deps/nghttp3/nghttp3.gyp:nghttp3' - ]} - ] - ]} - ], - [ 'node_shared_brotli=="false"', { 'dependencies': [ 'deps/brotli/brotli.gyp:brotli' ], }], diff --git a/src/async_wrap.h b/src/async_wrap.h index 76de08053b5..522779d57a0 100644 --- a/src/async_wrap.h +++ b/src/async_wrap.h @@ -60,12 +60,6 @@ namespace node { V(PROCESSWRAP) \ V(PROMISE) \ V(QUERYWRAP) \ - V(QLOGSTREAM) \ - V(QUICCLIENTSESSION) \ - V(QUICSERVERSESSION) \ - V(QUICSENDWRAP) \ - V(QUICSOCKET) \ - V(QUICSTREAM) \ V(SHUTDOWNWRAP) \ V(SIGNALWRAP) \ V(STATWATCHER) \ diff --git a/src/env.h b/src/env.h index 514a3871b4e..829e2f9fb07 100644 --- a/src/env.h +++ b/src/env.h @@ -366,7 +366,6 @@ constexpr size_t kFsStatsBufferLength = V(pubkey_string, "pubkey") \ V(public_exponent_string, "publicExponent") \ V(query_string, "query") \ - V(http3_alpn_string, "h3-29") \ V(rate_string, "rate") \ V(raw_string, "raw") \ V(read_host_object_string, "_readHostObject") \ @@ -435,16 +434,6 @@ constexpr size_t kFsStatsBufferLength = V(x_forwarded_string, "x-forwarded-for") \ V(zero_return_string, "ZERO_RETURN") -#if defined(NODE_EXPERIMENTAL_QUIC) && NODE_EXPERIMENTAL_QUIC -# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) \ - V(quicclientsession_instance_template, v8::ObjectTemplate) \ - V(quicserversession_instance_template, v8::ObjectTemplate) \ - V(quicserverstream_instance_template, v8::ObjectTemplate) \ - V(quicsocketsendwrap_instance_template, v8::ObjectTemplate) -#else -# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) -#endif - #define ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) \ V(async_wrap_ctor_template, v8::FunctionTemplate) \ V(async_wrap_object_ctor_template, v8::FunctionTemplate) \ @@ -479,34 +468,7 @@ constexpr size_t kFsStatsBufferLength = V(tty_constructor_template, v8::FunctionTemplate) \ V(write_wrap_template, v8::ObjectTemplate) \ V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) \ - V(x509_constructor_template, v8::FunctionTemplate) \ - QUIC_ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) - -#if defined(NODE_EXPERIMENTAL_QUIC) && NODE_EXPERIMENTAL_QUIC -# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ - V(quic_on_socket_close_function, v8::Function) \ - V(quic_on_socket_server_busy_function, v8::Function) \ - V(quic_on_session_cert_function, v8::Function) \ - V(quic_on_session_client_hello_function, v8::Function) \ - V(quic_on_session_close_function, v8::Function) \ - V(quic_on_session_handshake_function, v8::Function) \ - V(quic_on_session_keylog_function, v8::Function) \ - V(quic_on_session_path_validation_function, v8::Function) \ - V(quic_on_session_use_preferred_address_function, v8::Function) \ - V(quic_on_session_qlog_function, v8::Function) \ - V(quic_on_session_ready_function, v8::Function) \ - V(quic_on_session_status_function, v8::Function) \ - V(quic_on_session_ticket_function, v8::Function) \ - V(quic_on_session_version_negotiation_function, v8::Function) \ - V(quic_on_stream_close_function, v8::Function) \ - V(quic_on_stream_error_function, v8::Function) \ - V(quic_on_stream_ready_function, v8::Function) \ - V(quic_on_stream_reset_function, v8::Function) \ - V(quic_on_stream_headers_function, v8::Function) \ - V(quic_on_stream_blocked_function, v8::Function) -#else -# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) -#endif + V(x509_constructor_template, v8::FunctionTemplate) #define ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ V(async_hooks_after_function, v8::Function) \ @@ -563,8 +525,7 @@ constexpr size_t kFsStatsBufferLength = V(tls_wrap_constructor_function, v8::Function) \ V(trace_category_state_function, v8::Function) \ V(udp_constructor_function, v8::Function) \ - V(url_constructor_function, v8::Function) \ - QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) + V(url_constructor_function, v8::Function) class Environment; struct AllocatedBuffer; diff --git a/src/node_binding.cc b/src/node_binding.cc index 0db930adff1..6c7ab4b21ed 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -14,12 +14,6 @@ #define NODE_BUILTIN_OPENSSL_MODULES(V) #endif -#if defined(NODE_EXPERIMENTAL_QUIC) && NODE_EXPERIMENTAL_QUIC -#define NODE_BUILTIN_QUIC_MODULES(V) V(quic) -#else -#define NODE_BUILTIN_QUIC_MODULES(V) -#endif - #if NODE_HAVE_I18N_SUPPORT #define NODE_BUILTIN_ICU_MODULES(V) V(icu) #else @@ -98,7 +92,6 @@ #define NODE_BUILTIN_MODULES(V) \ NODE_BUILTIN_STANDARD_MODULES(V) \ NODE_BUILTIN_OPENSSL_MODULES(V) \ - NODE_BUILTIN_QUIC_MODULES(V) \ NODE_BUILTIN_ICU_MODULES(V) \ NODE_BUILTIN_PROFILER_MODULES(V) \ NODE_BUILTIN_DTRACE_MODULES(V) diff --git a/src/node_errors.h b/src/node_errors.h index 6158a968d27..984603c42e2 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -78,8 +78,6 @@ void OnFatalError(const char* location, const char* message); V(ERR_WASI_NOT_STARTED, Error) \ V(ERR_WORKER_INIT_FAILED, Error) \ V(ERR_PROTO_ACCESS, Error) \ - V(ERR_QUIC_CANNOT_SET_GROUPS, Error) \ - V(ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT, Error) #define V(code, type) \ inline v8::Local<v8::Value> code(v8::Isolate* isolate, \ @@ -156,9 +154,7 @@ void OnFatalError(const char* location, const char* message); V(ERR_WORKER_INIT_FAILED, "Worker initialization failure") \ V(ERR_PROTO_ACCESS, \ "Accessing Object.prototype.__proto__ has been " \ - "disallowed with --disable-proto=throw") \ - V(ERR_QUIC_CANNOT_SET_GROUPS, "Cannot set groups") \ - V(ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT, "Failure setting SNI context") + "disallowed with --disable-proto=throw") #define V(code, message) \ inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \ diff --git a/src/node_http_common.h b/src/node_http_common.h index 8017c0d7aad..ad9f2a864e0 100644 --- a/src/node_http_common.h +++ b/src/node_http_common.h @@ -270,7 +270,7 @@ class NgHeaders { MaybeStackBuffer<char, 3000> buf_; }; -// The ng libraries (nghttp2 and nghttp3) each use nearly identical +// The ng libraries use nearly identical // reference counted structures for retaining header name and value // information in memory until the application is done with it. // The NgRcBufPointer is an intelligent pointer capable of working diff --git a/src/node_mem.h b/src/node_mem.h index f8cdc20848f..332d1cd1eff 100644 --- a/src/node_mem.h +++ b/src/node_mem.h @@ -8,7 +8,7 @@ namespace node { namespace mem { -// Both ngtcp2 and nghttp2 allow custom allocators that +// nghttp2 allows custom allocators that // follow exactly the same structure and behavior, but // use different struct names. To allow for code re-use, // the NgLibMemoryManager template class can be used for both. diff --git a/src/node_metadata.cc b/src/node_metadata.cc index e8864d35527..8d0a725de45 100644 --- a/src/node_metadata.cc +++ b/src/node_metadata.cc @@ -13,11 +13,6 @@ #include <openssl/opensslv.h> #endif // HAVE_OPENSSL -#if defined(NODE_EXPERIMENTAL_QUIC) && NODE_EXPERIMENTAL_QUIC -#include <ngtcp2/version.h> -#include <nghttp3/version.h> -#endif - #ifdef NODE_HAVE_I18N_SUPPORT #include <unicode/timezone.h> #include <unicode/ulocdata.h> @@ -96,11 +91,6 @@ Metadata::Versions::Versions() { openssl = GetOpenSSLVersion(); #endif -#if defined(NODE_EXPERIMENTAL_QUIC) && NODE_EXPERIMENTAL_QUIC - ngtcp2 = NGTCP2_VERSION; - nghttp3 = NGHTTP3_VERSION; -#endif - #ifdef NODE_HAVE_I18N_SUPPORT icu = U_ICU_VERSION; unicode = U_UNICODE_VERSION; diff --git a/src/node_metadata.h b/src/node_metadata.h index 2a4571883d8..bf7e5d3ff4e 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -38,12 +38,6 @@ namespace node { #define NODE_VERSIONS_KEY_CRYPTO(V) #endif -#if defined(NODE_EXPERIMENTAL_QUIC) && NODE_EXPERIMENTAL_QUIC -#define NODE_VERSIONS_KEY_QUIC(V) V(ngtcp2) V(nghttp3) -#else -#define NODE_VERSIONS_KEY_QUIC(V) -#endif - #ifdef NODE_HAVE_I18N_SUPPORT #define NODE_VERSIONS_KEY_INTL(V) \ V(cldr) \ @@ -57,7 +51,6 @@ namespace node { #define NODE_VERSIONS_KEYS(V) \ NODE_VERSIONS_KEYS_BASE(V) \ NODE_VERSIONS_KEY_CRYPTO(V) \ - NODE_VERSIONS_KEY_QUIC(V) \ NODE_VERSIONS_KEY_INTL(V) class Metadata { diff --git a/src/node_native_module.cc b/src/node_native_module.cc index f7d73544d2d..5a8cf12347d 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -105,10 +105,6 @@ void NativeModuleLoader::InitializeModuleCategories() { "internal/process/policy", "internal/streams/lazy_transform", #endif // !HAVE_OPENSSL -#if !NODE_EXPERIMENTAL_QUIC - "internal/quic/core", - "internal/quic/util", -#endif "sys", // Deprecated. "wasi", // Experimental. "internal/test/binding", diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc deleted file mode 100644 index f34c8669f94..00000000000 --- a/src/quic/node_quic.cc +++ /dev/null @@ -1,271 +0,0 @@ -#include "debug_utils-inl.h" -#include "node.h" -#include "env-inl.h" -#include "crypto/crypto_context.h" -#include "crypto/crypto_common.h" -#include "node_errors.h" -#include "node_process.h" -#include "node_quic_crypto.h" -#include "node_quic_session-inl.h" -#include "node_quic_socket-inl.h" -#include "node_quic_stream-inl.h" -#include "node_quic_state.h" -#include "node_quic_util-inl.h" -#include "node_sockaddr-inl.h" -#include "nghttp3/nghttp3.h" - -#include <memory> -#include <utility> - -namespace node { - -using crypto::SecureContext; -using v8::Context; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::HandleScope; -using v8::Isolate; -using v8::Local; -using v8::Object; -using v8::Value; - -namespace quic { - -constexpr FastStringKey QuicState::binding_data_name; - -void QuicState::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("root_buffer", root_buffer); -} - -namespace { -// Register the JavaScript callbacks the internal binding will use to report -// status and updates. This is called only once when the quic module is loaded. -void QuicSetCallbacks(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsObject()); - Local<Object> obj = args[0].As<Object>(); - -#define SETFUNCTION(name, callback) \ - do { \ - Local<Value> fn; \ - CHECK(obj->Get(env->context(), \ - FIXED_ONE_BYTE_STRING(env->isolate(), name)).ToLocal(&fn));\ - CHECK(fn->IsFunction()); \ - env->set_quic_on_##callback##_function(fn.As<Function>()); \ - } while (0) - - SETFUNCTION("onSocketClose", socket_close); - SETFUNCTION("onSessionReady", session_ready); - SETFUNCTION("onSessionCert", session_cert); - SETFUNCTION("onSessionClientHello", session_client_hello); - SETFUNCTION("onSessionClose", session_close); - SETFUNCTION("onSessionHandshake", session_handshake); - SETFUNCTION("onSessionKeylog", session_keylog); - SETFUNCTION("onSessionUsePreferredAddress", session_use_preferred_address); - SETFUNCTION("onSessionPathValidation", session_path_validation); - SETFUNCTION("onSessionQlog", session_qlog); - SETFUNCTION("onSessionStatus", session_status); - SETFUNCTION("onSessionTicket", session_ticket); - SETFUNCTION("onSessionVersionNegotiation", session_version_negotiation); - SETFUNCTION("onStreamReady", stream_ready); - SETFUNCTION("onStreamClose", stream_close); - SETFUNCTION("onStreamError", stream_error); - SETFUNCTION("onStreamReset", stream_reset); - SETFUNCTION("onSocketServerBusy", socket_server_busy); - SETFUNCTION("onStreamHeaders", stream_headers); - SETFUNCTION("onStreamBlocked", stream_blocked); - -#undef SETFUNCTION -} - -// Sets QUIC specific configuration options for the SecureContext. -// It's entirely likely that there's a better way to do this, but -// for now this works. -template <ngtcp2_crypto_side side> -void QuicInitSecureContext(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsObject()); // Secure Context - CHECK(args[1]->IsString()); // groups - CHECK(args[2]->IsBoolean()); // early data - - SecureContext* sc; - ASSIGN_OR_RETURN_UNWRAP(&sc, args[0].As<Object>(), - args.GetReturnValue().Set(UV_EBADF)); - const node::Utf8Value groups(env->isolate(), args[1]); - - bool early_data = args[2]->BooleanValue(env->isolate()); - - InitializeSecureContext( - BaseObjectPtr<SecureContext>(sc), - early_data, - side); - - if (!crypto::SetGroups(sc, *groups)) - THROW_ERR_QUIC_CANNOT_SET_GROUPS(env); -} -} // namespace - -void Initialize(Local<Object> target, - Local<Value> unused, - Local<Context> context, - void* priv) { - Environment* env = Environment::GetCurrent(context); - Isolate* isolate = env->isolate(); - HandleScope handle_scope(isolate); - - HistogramBase::Initialize(env); - - QuicState* const state = - env->AddBindingData<QuicState>(context, target); - if (state == nullptr) return; - -#define SET_STATE_TYPEDARRAY(name, field) \ - target->Set(context, \ - FIXED_ONE_BYTE_STRING(isolate, (name)), \ - (field.GetJSArray())).Check() - SET_STATE_TYPEDARRAY("sessionConfig", state->quicsessionconfig_buffer); - SET_STATE_TYPEDARRAY("http3Config", state->http3config_buffer); -#undef SET_STATE_TYPEDARRAY - - QuicSocket::Initialize(env, target, context); - QuicEndpoint::Initialize(env, target, context); - QuicSession::Initialize(env, target, context); - QuicStream::Initialize(env, target, context); - - env->SetMethod(target, - "setCallbacks", - QuicSetCallbacks); - env->SetMethod(target, - "initSecureContext", - QuicInitSecureContext<NGTCP2_CRYPTO_SIDE_SERVER>); - env->SetMethod(target, - "initSecureContextClient", - QuicInitSecureContext<NGTCP2_CRYPTO_SIDE_CLIENT>); - - Local<Object> constants = Object::New(env->isolate()); - -// TODO(@jasnell): Audit which constants are actually being used in JS -#define QUIC_CONSTANTS(V) \ - V(DEFAULT_MAX_STREAM_DATA_BIDI_LOCAL) \ - V(DEFAULT_RETRYTOKEN_EXPIRATION) \ - V(DEFAULT_MAX_CONNECTIONS) \ - V(DEFAULT_MAX_CONNECTIONS_PER_HOST) \ - V(DEFAULT_MAX_STATELESS_RESETS_PER_HOST) \ - V(IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY) \ - V(IDX_HTTP3_QPACK_BLOCKED_STREAMS) \ - V(IDX_HTTP3_MAX_HEADER_LIST_SIZE) \ - V(IDX_HTTP3_MAX_PUSHES) \ - V(IDX_HTTP3_MAX_HEADER_PAIRS) \ - V(IDX_HTTP3_MAX_HEADER_LENGTH) \ - V(IDX_HTTP3_CONFIG_COUNT) \ - V(IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT) \ - V(IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) \ - V(IDX_QUIC_SESSION_MAX_DATA) \ - V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL) \ - V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE) \ - V(IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI) \ - V(IDX_QUIC_SESSION_MAX_STREAMS_BIDI) \ - V(IDX_QUIC_SESSION_MAX_STREAMS_UNI) \ - V(IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE) \ - V(IDX_QUIC_SESSION_ACK_DELAY_EXPONENT) \ - V(IDX_QUIC_SESSION_DISABLE_MIGRATION) \ - V(IDX_QUIC_SESSION_MAX_ACK_DELAY) \ - V(IDX_QUIC_SESSION_CC_ALGO) \ - V(IDX_QUIC_SESSION_CONFIG_COUNT) \ - V(MAX_RETRYTOKEN_EXPIRATION) \ - V(MIN_RETRYTOKEN_EXPIRATION) \ - V(NGTCP2_APP_NOERROR) \ - V(NGTCP2_PATH_VALIDATION_RESULT_FAILURE) \ - V(NGTCP2_PATH_VALIDATION_RESULT_SUCCESS) \ - V(NGTCP2_CC_ALGO_CUBIC) \ - V(NGTCP2_CC_ALGO_RENO) \ - V(QUIC_ERROR_APPLICATION) \ - V(QUIC_ERROR_CRYPTO) \ - V(QUIC_ERROR_SESSION) \ - V(QUIC_PREFERRED_ADDRESS_USE) \ - V(QUIC_PREFERRED_ADDRESS_IGNORE) \ - V(QUICCLIENTSESSION_OPTION_REQUEST_OCSP) \ - V(QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY) \ - V(QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED) \ - V(QUICSERVERSESSION_OPTION_REQUEST_CERT) \ - V(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS) \ - V(QUICSTREAM_HEADER_FLAGS_NONE) \ - V(QUICSTREAM_HEADER_FLAGS_TERMINAL) \ - V(QUICSTREAM_HEADERS_KIND_NONE) \ - V(QUICSTREAM_HEADERS_KIND_INFORMATIONAL) \ - V(QUICSTREAM_HEADERS_KIND_PUSH) \ - V(QUICSTREAM_HEADERS_KIND_INITIAL) \ - V(QUICSTREAM_HEADERS_KIND_TRAILING) \ - V(ERR_FAILED_TO_CREATE_SESSION) \ - V(UV_EBADF) - -#define V(id, _, __) \ - NODE_DEFINE_CONSTANT(constants, IDX_QUICSESSION_STATE_##id); - QUICSESSION_SHARED_STATE(V) -#undef V - -#define V(id, _, __) \ - NODE_DEFINE_CONSTANT(constants, IDX_QUICSOCKET_STATE_##id); - QUICSOCKET_SHARED_STATE(V) -#undef V - -#define V(id, _, __) \ - NODE_DEFINE_CONSTANT(constants, IDX_QUICSTREAM_STATE_##id); - QUICSTREAM_SHARED_STATE(V) -#undef V - -#define V(name, _, __) \ - NODE_DEFINE_CONSTANT(constants, IDX_QUIC_SESSION_STATS_##name); - SESSION_STATS(V) -#undef V - -#define V(name, _, __) \ - NODE_DEFINE_CONSTANT(constants, IDX_QUIC_SOCKET_STATS_##name); - SOCKET_STATS(V) -#undef V - -#define V(name, _, __) \ - NODE_DEFINE_CONSTANT(constants, IDX_QUIC_STREAM_STATS_##name); - STREAM_STATS(V) -#undef V - -#define V(name) NODE_DEFINE_CONSTANT(constants, name); - QUIC_CONSTANTS(V) -#undef V - - NODE_DEFINE_CONSTANT(constants, NGTCP2_DEFAULT_MAX_PKTLEN); - NODE_DEFINE_CONSTANT(constants, NGTCP2_PROTO_VER); - NODE_DEFINE_CONSTANT(constants, NGTCP2_DEFAULT_MAX_ACK_DELAY); - NODE_DEFINE_CONSTANT(constants, NGTCP2_MAX_CIDLEN); - NODE_DEFINE_CONSTANT(constants, NGTCP2_MIN_CIDLEN); - - NODE_DEFINE_CONSTANT(constants, NGTCP2_NO_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_INTERNAL_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_CONNECTION_REFUSED); - NODE_DEFINE_CONSTANT(constants, NGTCP2_FLOW_CONTROL_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_STREAM_LIMIT_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_STREAM_STATE_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_FINAL_SIZE_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_FRAME_ENCODING_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_TRANSPORT_PARAMETER_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_CONNECTION_ID_LIMIT_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_PROTOCOL_VIOLATION); - NODE_DEFINE_CONSTANT(constants, NGTCP2_INVALID_TOKEN); - NODE_DEFINE_CONSTANT(constants, NGTCP2_APPLICATION_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_CRYPTO_BUFFER_EXCEEDED); - NODE_DEFINE_CONSTANT(constants, NGTCP2_KEY_UPDATE_ERROR); - NODE_DEFINE_CONSTANT(constants, NGTCP2_CRYPTO_ERROR); - - NODE_DEFINE_CONSTANT(constants, AF_INET); - NODE_DEFINE_CONSTANT(constants, AF_INET6); - NODE_DEFINE_STRING_CONSTANT(constants, - NODE_STRINGIFY_HELPER(NGHTTP3_ALPN_H3), - NGHTTP3_ALPN_H3); - - target->Set(context, env->constants_string(), constants).FromJust(); -} - -} // namespace quic -} // namespace node - -NODE_MODULE_CONTEXT_AWARE_INTERNAL(quic, node::quic::Initialize) diff --git a/src/quic/node_quic_buffer-inl.h b/src/quic/node_quic_buffer-inl.h deleted file mode 100644 index 25f130ac9fe..00000000000 --- a/src/quic/node_quic_buffer-inl.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_BUFFER_INL_H_ -#define SRC_QUIC_NODE_QUIC_BUFFER_INL_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "node_quic_buffer.h" -#include "node_bob-inl.h" -#include "util-inl.h" -#include "uv.h" - -#include <algorithm> - -namespace node { - -namespace quic { - -QuicBufferChunk::QuicBufferChunk(size_t len) - : data_buf_(len, 0), - buf_(uv_buf_init(reinterpret_cast<char*>(data_buf_.data()), len)), - length_(len), - done_called_(true) {} - -QuicBufferChunk::QuicBufferChunk(uv_buf_t buf, DoneCB done) - : buf_(buf), - length_(buf.len) { - if (done != nullptr) - done_ = std::move(done); -} - -QuicBufferChunk::~QuicBufferChunk() { - CHECK(done_called_); -} - -size_t QuicBufferChunk::Seek(size_t amount) { - amount = std::min(amount, remaining()); - buf_.base += amount; - buf_.len -= amount; - return amount; -} - -size_t QuicBufferChunk::Consume(size_t amount) { - amount = std::min(amount, length_); - length_ -= amount; - return amount; -} - -void QuicBufferChunk::Done(int status) { - if (done_called_) return; - done_called_ = true; - if (done_ != nullptr) - std::move(done_)(status); -} - -QuicBuffer::QuicBuffer(QuicBuffer&& src) noexcept - : head_(src.head_), - tail_(src.tail_), - ended_(src.ended_), - length_(src.length_) { - root_ = std::move(src.root_); - src.head_ = nullptr; - src.tail_ = nullptr; - src.length_ = 0; - src.remaining_ = 0; - src.ended_ = false; -} - -QuicBuffer& QuicBuffer::operator=(QuicBuffer&& src) noexcept { - if (this == &src) return *this; - this->~QuicBuffer(); - return *new(this) QuicBuffer(std::move(src)); -} - -bool QuicBuffer::is_empty(uv_buf_t buf) { - DCHECK_IMPLIES(buf.base == nullptr, buf.len == 0); - return buf.len == 0; -} - -size_t QuicBuffer::Consume(size_t amount) { - return Consume(0, amount); -} - -size_t QuicBuffer::Cancel(int status) { - if (canceled_) return 0; - canceled_ = true; - size_t t = Consume(status, length()); - return t; -} - -void QuicBuffer::Push(uv_buf_t buf, DoneCB done) { - std::unique_ptr<QuicBufferChunk> chunk = - std::make_unique<QuicBufferChunk>(buf, done); - Push(std::move(chunk)); -} -} // namespace quic -} // namespace node - -#endif // NODE_WANT_INTERNALS -#endif // SRC_QUIC_NODE_QUIC_BUFFER_INL_H_ diff --git a/src/quic/node_quic_buffer.cc b/src/quic/node_quic_buffer.cc deleted file mode 100644 index ebe230271ac..00000000000 --- a/src/quic/node_quic_buffer.cc +++ /dev/null @@ -1,166 +0,0 @@ -#include "node_quic_buffer-inl.h" // NOLINT(build/include) -#include "node_bob-inl.h" -#include "util.h" -#include "uv.h" - -#include <algorithm> -#include <memory> -#include <utility> - -namespace node { -namespace quic { - -void QuicBufferChunk::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("buf", data_buf_); - tracker->TrackField("next", next_); -} - -size_t QuicBuffer::Push(uv_buf_t* bufs, size_t nbufs, DoneCB done) { - size_t len = 0; - if (UNLIKELY(nbufs == 0)) { - done(0); - return 0; - } - DCHECK_NOT_NULL(bufs); - size_t n = 0; - while (nbufs > 1) { - if (!is_empty(bufs[n])) { - Push(bufs[n]); - len += bufs[n].len; - } - n++; - nbufs--; - } - if (!is_empty(bufs[n])) { - Push(bufs[n], done); - len += bufs[n].len; - } - // Special case if all the bufs were empty. - if (UNLIKELY(len == 0)) - done(0); - - return len; -} - -void QuicBuffer::Push(std::unique_ptr<QuicBufferChunk> chunk) { - CHECK(!ended_); - length_ += chunk->remaining(); - remaining_ += chunk->remaining(); - if (!tail_) { - root_ = std::move(chunk); - head_ = tail_ = root_.get(); - } else { - tail_->next_ = std::move(chunk); - tail_ = tail_->next_.get(); - if (!head_) - head_ = tail_; - } -} - -size_t QuicBuffer::Seek(size_t amount) { - size_t len = 0; - while (head_ && amount > 0) { - size_t amt = head_->Seek(amount); - amount -= amt; - len += amt; - remaining_ -= amt; - if (head_->remaining()) - break; - head_ = head_->next_.get(); - } - return len; -} - -bool QuicBuffer::Pop(int status) { - if (!root_) - return false; - std::unique_ptr<QuicBufferChunk> root(std::move(root_)); - root_ = std::move(root.get()->next_); - - if (head_ == root.get()) - head_ = root_.get(); - if (tail_ == root.get()) - tail_ = root_.get(); - - root->Done(status); - return true; -} - -size_t QuicBuffer::Consume(int status, size_t amount) { - size_t amt = std::min(amount, length_); - size_t len = 0; - while (root_ && amt > 0) { - auto root = root_.get(); - size_t consumed = root->Consume(amt); - len += consumed; - length_ -= consumed; - amt -= consumed; - if (root->length() > 0) - break; - Pop(status); - } - return len; -} - -void QuicBuffer::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("root", root_); -} - -int QuicBuffer::DoPull( - bob::Next<ngtcp2_vec> next, - int options, - ngtcp2_vec* data, - size_t count, - size_t max_count_hint) { - size_t len = 0; - size_t numbytes = 0; - int status = bob::Status::STATUS_CONTINUE; - - // There's no data to read. - if (!remaining() || head_ == nullptr) { - status = is_ended() ? - bob::Status::STATUS_END : - bob::Status::STATUS_BLOCK; - std::move(next)(status, nullptr, 0, [](size_t len) {}); - return status; - } - - // Ensure that there's storage space. - MaybeStackBuffer<ngtcp2_vec, kMaxVectorCount> vec; - if (data == nullptr || count == 0) { - vec.AllocateSufficientStorage(max_count_hint); - data = vec.out(); - } else { - max_count_hint = std::min(count, max_count_hint); - } - - // Build the list of buffers. - QuicBufferChunk* pos = head_; - while (pos != nullptr && len < max_count_hint) { - data[len].base = reinterpret_cast<uint8_t*>(pos->buf().base); - data[len].len = pos->buf().len; - numbytes += data[len].len; - len++; - pos = pos->next_.get(); - } - - // If the buffer is ended, and the number of bytes - // matches the total remaining and OPTIONS_END is - // used, set the status to STATUS_END. - if (is_ended() && - numbytes == remaining() && - options & bob::OPTIONS_END) - status = bob::Status::STATUS_END; - - // Pass the data back out to the caller. - std::move(next)( - status, - data, - len, - [this](size_t len) { Seek(len); }); - - return status; -} - -} // namespace quic -} // namespace node diff --git a/src/quic/node_quic_buffer.h b/src/quic/node_quic_buffer.h deleted file mode 100644 index 3d81a28176d..00000000000 --- a/src/quic/node_quic_buffer.h +++ /dev/null @@ -1,239 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_BUFFER_H_ -#define SRC_QUIC_NODE_QUIC_BUFFER_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "memory_tracker-inl.h" -#include "ngtcp2/ngtcp2.h" -#include "node.h" -#include "node_bob.h" -#include "node_internals.h" -#include "util.h" -#include "uv.h" - -#include <vector> - -namespace node { -namespace quic { - -class QuicBuffer; - -constexpr size_t kMaxVectorCount = 16; - -using DoneCB = std::function<void(int)>; - -// When data is sent over QUIC, we are required to retain it in memory -// until we receive an acknowledgement that it has been successfully -// received. The QuicBuffer object is what we use to handle that -// and track until it is acknowledged. To understand the QuicBuffer -// object itself, it is important to understand how ngtcp2 and nghttp3 -// handle data that is given to it to serialize into QUIC packets. -// -// An individual QUIC packet may contain multiple QUIC frames. Whenever -// we create a QUIC packet, we really have no idea what frames are going -// to be encoded or how much buffered handshake or stream data is going -// to be included within that QuicPacket. If there is buffered data -// available for a stream, we provide an array of pointers to that data -// and an indication about how much data is available, then we leave it -// entirely up to ngtcp2 and nghttp3 to determine how much of the data -// to encode into the QUIC packet. It is only *after* the QUIC packet -// is encoded that we can know how much was actually written. -// -// Once written to a QUIC Packet, we have to keep the data in memory -// until an acknowledgement is received. In QUIC, acknowledgements are -// received per range of packets. -// -// QuicBuffer is complicated because it needs to be able to accomplish -// three things: (a) buffering uv_buf_t instances passed down from -// JavaScript without memcpy and keeping track of the Write callback -// associated with each, (b) tracking what data has already been -// encoded in a QUIC packet and what data is remaining to be read, and -// (c) tracking which data has been acknowledged and which hasn't. -// QuicBuffer is further complicated by design quirks and limitations -// of the StreamBase API and how it interacts with the JavaScript side. -// -// QuicBuffer is a linked list of QuicBufferChunk instances. -// A single QuicBufferChunk wraps a single non-zero-length uv_buf_t. -// When the QuicBufferChunk is created, we capture the total length -// of the buffer and the total number of bytes remaining to be sent. -// Initially, these numbers are identical. -// -// When data is encoded into a QuicPacket, we advance the QuicBufferChunk's -// remaining-to-be-read by the number of bytes actually encoded. If there -// are no more bytes remaining to be encoded, we move to the next chunk -// in the linked list. -// -// When an acknowledgement is received, we decrement the QuicBufferChunk's -// length by the number of acknowledged bytes. Once the unacknowledged -// length reaches 0, we invoke the callback function associated with the -// QuicBufferChunk (if any). -// -// QuicStream is a StreamBase implementation. For every DoWrite call, -// it receives one or more uv_buf_t instances in a single batch associated -// with a single write callback. For each uv_buf_t DoWrite gets, a -// corresponding QuicBufferChunk is added to the QuicBuffer, with the -// callback associated with the final chunk added to the list. - - -// A QuicBufferChunk contains the actual buffered data -// along with a callback to be called when the data has -// been consumed. -// -// Any given chunk has a remaining-to-be-acknowledged length (length()) and a -// remaining-to-be-read-length (remaining()). The former tracks the number -// of bytes that have yet to be acknowledged by the QUIC peer. Once the -// remaining-to-be-acknowledged length reaches zero, the done callback -// associated with the QuicBufferChunk can be called and the QuicBufferChunk -// can be discarded. The remaining-to-be-read length is adjusted as data is -// serialized into QUIC packets and sent. -// The remaining-to-be-acknowledged length is adjusted using consume(), -// while the remaining-to-be-ead length is adjusted using seek(). -class QuicBufferChunk final : public MemoryRetainer { - public: - // Default non-op done handler. - static void default_done(int status) {} - - // In this variant, the QuicBufferChunk owns the underlying - // data storage within a vector. The data will be - // freed when the QuicBufferChunk is destroyed. - inline explicit QuicBufferChunk(size_t len); - - // In this variant, the QuicBufferChunk only maintains a - // pointer to the underlying data buffer. The QuicBufferChunk - // does not take ownership of the buffer. The done callback - // is invoked to let the caller know when the chunk is no - // longer being used. - inline QuicBufferChunk(uv_buf_t buf_, DoneCB done_); - - inline ~QuicBufferChunk() override; - - // Invokes the done callback associated with the QuicBufferChunk. - inline void Done(int status); - - // length() provides the remaining-to-be-acknowledged length. - // The QuicBufferChunk must be retained in memory while this - // count is greater than zero. The length is adjusted by - // calling Consume(); - inline size_t length() const { return length_; } - - // remaining() provides the remaining-to-be-read length number of bytes. - // The length is adjusted by calling Seek() - inline size_t remaining() const { return buf_.len; } - - // Consumes (acknowledges) the given number of bytes. If amount - // is greater than length(), only length() bytes are consumed. - // Returns the actual number of bytes consumed. - inline size_t Consume(size_t amount); - - // Seeks (reads) the given number of bytes. If amount is greater - // than remaining(), only remaining() bytes are read. Returns - // the actual number of bytes read. - inline size_t Seek(size_t amount); - - uint8_t* out() { return reinterpret_cast<uint8_t*>(buf_.base); } - uv_buf_t buf() { return buf_; } - const uv_buf_t buf() const { return buf_; } - - void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(QuicBufferChunk) - SET_SELF_SIZE(QuicBufferChunk) - - private: - std::vector<uint8_t> data_buf_; - uv_buf_t buf_; - DoneCB done_ = default_done; - size_t length_ = 0; - bool done_called_ = false; - std::unique_ptr<QuicBufferChunk> next_; - - friend class QuicBuffer; -}; - -class QuicBuffer final : public bob::SourceImpl<ngtcp2_vec>, - public MemoryRetainer { - public: - QuicBuffer() = default; - - inline QuicBuffer(QuicBuffer&& src) noexcept; - inline QuicBuffer& operator=(QuicBuffer&& src) noexcept; - - ~QuicBuffer() override { - Cancel(); // Cancel the remaining data - CHECK_EQ(length_, 0); - } - - // Marks the QuicBuffer as having ended, preventing new QuicBufferChunk - // instances from being appended to the linked list and allowing the - // Pull operation to know when to signal that the flow of data is - // completed. - void End() { ended_ = true; } - bool is_ended() const { return ended_; } - - // Push one or more uv_buf_t instances into the buffer. - // the DoneCB callback will be invoked when the last - // uv_buf_t in the bufs array is consumed and popped out - // of the internal linked list. Ownership of the uv_buf_t - // remains with the caller. - size_t Push( - uv_buf_t* bufs, - size_t nbufs, - DoneCB done = QuicBufferChunk::default_done); - - // Pushes a single QuicBufferChunk into the linked list - void Push(std::unique_ptr<QuicBufferChunk> chunk); - - // Consume the given number of bytes within the buffer. If amount - // is greater than length(), length() bytes are consumed. Returns - // the actual number of bytes consumed. - inline size_t Consume(size_t amount); - - // Cancels the remaining bytes within the buffer. - inline size_t Cancel(int status = UV_ECANCELED); - - // Seeks (reads) the given number of bytes. If amount is greater - // than remaining(), seeks remaining() bytes. Returns the actual - // number of bytes read. - size_t Seek(size_t amount); - - // The total number of unacknowledged bytes remaining. - size_t length() const { return length_; } - - // The total number of unread bytes remaining. - size_t remaining() const { return remaining_; } - - void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(QuicBuffer); - SET_SELF_SIZE(QuicBuffer); - - protected: - int DoPull( - bob::Next<ngtcp2_vec> next, - int options, - ngtcp2_vec* data, - size_t count, - size_t max_count_hint) override; - - private: - inline static bool is_empty(uv_buf_t buf); - size_t Consume(int status, size_t amount); - bool Pop(int status = 0); - inline void Push(uv_buf_t buf, DoneCB done = nullptr); - - std::unique_ptr<QuicBufferChunk> root_; - QuicBufferChunk* head_ = nullptr; // Current Read Position - QuicBufferChunk* tail_ = nullptr; // Current Write Position - - bool canceled_ = false; - bool ended_ = false; - size_t length_ = 0; - size_t remaining_ = 0; - - friend class QuicBufferChunk; -}; - -} // namespace quic -} // namespace node - -#endif // NODE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_BUFFER_H_ diff --git a/src/quic/node_quic_crypto.cc b/src/quic/node_quic_crypto.cc deleted file mode 100644 index 74bb4f18783..00000000000 --- a/src/quic/node_quic_crypto.cc +++ /dev/null @@ -1,748 +0,0 @@ -#include "node_quic_crypto.h" -#include "env-inl.h" -#include "node_crypto.h" -#include "crypto/crypto_util.h" -#include "crypto/crypto_context.h" -#include "crypto/crypto_common.h" -#include "node_process.h" -#include "node_quic_session-inl.h" -#include "node_quic_util-inl.h" -#include "node_sockaddr-inl.h" -#include "node_url.h" -#include "string_bytes.h" -#include "v8.h" -#include "util-inl.h" - -#include <ngtcp2/ngtcp2.h> -#include <ngtcp2/ngtcp2_crypto.h> -#include <nghttp3/nghttp3.h> // NGHTTP3_ALPN_H3 -#include <openssl/bio.h> -#include <openssl/err.h> -#include <openssl/evp.h> -#include <openssl/kdf.h> -#include <openssl/ssl.h> -#include <openssl/x509v3.h> - -#include <iterator> -#include <numeric> -#include <unordered_map> -#include <string> -#include <sstream> -#include <vector> - -namespace node { - -using crypto::EntropySource; -using v8::Local; -using v8::Value; - -namespace quic { - -bool SessionTicketAppData::Set(const uint8_t* data, size_t len) { - if (set_) - return false; - set_ = true; - SSL_SESSION_set1_ticket_appdata(session_, data, len); - return set_; -} - -bool SessionTicketAppData::Get(uint8_t** data, size_t* len) const { - return SSL_SESSION_get0_ticket_appdata( - session_, - reinterpret_cast<void**>(data), - len) == 1; -} - -namespace { -constexpr int kCryptoTokenKeylen = 32; -constexpr int kCryptoTokenIvlen = 32; - -// Used solely to derive the keys used to generate and -// validate retry tokens. The implementation of this is -// Node.js specific. We use the current implementation -// because it is simple. -bool DeriveTokenKey( - uint8_t* token_key, - uint8_t* token_iv, - const uint8_t* rand_data, - size_t rand_datalen, - const ngtcp2_crypto_ctx& ctx, - const uint8_t* token_secret) { - static constexpr int kCryptoTokenSecretlen = 32; - uint8_t secret[kCryptoTokenSecretlen]; - - return - NGTCP2_OK(ngtcp2_crypto_hkdf_extract( - secret, - &ctx.md, - token_secret, - kTokenSecretLen, - rand_data, - rand_datalen)) && - NGTCP2_OK(ngtcp2_crypto_derive_packet_protection_key( - token_key, - token_iv, - nullptr, - &ctx.aead, - &ctx.md, - secret, - kCryptoTokenSecretlen)); -} - -// Retry tokens are generated only by QUIC servers. They -// are opaque to QUIC clients and must not be guessable by -// on- or off-path attackers. A QUIC server sends a RETRY -// token as a way of initiating explicit path validation -// with a client in response to an initial QUIC packet. -// The client, upon receiving a RETRY, must abandon the -// initial connection attempt and try again, including the -// received retry token in the new initial packet sent to -// the server. If the server is performing explicit -// valiation, it will look for the presence of the retry -// token and validate it if found. The internal structure -// of the retry token must be meaningful to the server, -// and the server must be able to validate the token without -// relying on any state left over from the previous connection -// attempt. The implementation here is entirely Node.js -// specific. -// -// The token is generated by: -// 1. Appending the raw bytes of given socket address, the current -// timestamp, and the original CID together into a single byte -// array. -// 2. Generating a block of random data that is used together with -// the token secret to cryptographically derive an encryption key. -// 3. Encrypting the byte array from step 1 using the encryption key -// from step 2. -// 4. Appending random data generated in step 2 to the token. -// -// The token secret must be kept secret on the QUIC server that -// generated the retry. When multiple QUIC servers are used in a -// cluster, it cannot be guaranteed that the same QUIC server -// instance will receive the subsequent new Initial packet. Therefore, -// all QUIC servers in the cluster should either share or be aware -// of the same token secret or a mechanism needs to be implemented -// to ensure that subsequent packets are routed to the same QUIC -// server instance. -// -// A malicious peer could attempt to guess the token secret by -// sending a large number specially crafted RETRY-eliciting packets -// to a server then analyzing the resulting retry tokens. To reduce -// the possibility of such attacks, the current implementation of -// QuicSocket generates the token secret randomly for each instance, -// and the number of RETRY responses sent to a given remote address -// should be limited. Such attacks should be of little actual value -// in most cases. -bool GenerateRetryToken( - uint8_t* token, - size_t* tokenlen, - const SocketAddress& addr, - const QuicCID& ocid, - const uint8_t* token_secret) { - std::array<uint8_t, 4096> plaintext; - uint8_t rand_data[kTokenRandLen]; - uint8_t token_key[kCryptoTokenKeylen]; - uint8_t token_iv[kCryptoTokenIvlen]; - - ngtcp2_crypto_ctx ctx; - ngtcp2_crypto_ctx_initial(&ctx); - size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead); - uint64_t now = uv_hrtime(); - - auto p = std::begin(plaintext); - p = std::copy_n(addr.raw(), addr.length(), p); - p = std::copy_n(reinterpret_cast<uint8_t*>(&now), sizeof(uint64_t), p); - p = std::copy_n(ocid->data, ocid->datalen, p); - - EntropySource(rand_data, kTokenRandLen); - - if (!DeriveTokenKey( - token_key, - token_iv, - rand_data, - kTokenRandLen, - ctx, - token_secret)) { - return false; - } - - ngtcp2_crypto_aead_ctx aead_ctx; - if (NGTCP2_ERR(ngtcp2_crypto_aead_ctx_encrypt_init( - &aead_ctx, - &ctx.aead, - token_key, - ivlen))) { - return false; - } - - size_t plaintextlen = std::distance(std::begin(plaintext), p); - if (NGTCP2_ERR(ngtcp2_crypto_encrypt( - token, - &ctx.aead, - &aead_ctx, - plaintext.data(), - plaintextlen, - token_iv, - ivlen, - addr.raw(), - addr.length()))) { - return false; - } - - *tokenlen = plaintextlen + ngtcp2_crypto_aead_taglen(&ctx.aead); - memcpy(token + (*tokenlen), rand_data, kTokenRandLen); - *tokenlen += kTokenRandLen; - return true; -} -} // namespace - -// A stateless reset token is used when a QUIC endpoint receives a -// QUIC packet with a short header but the associated connection ID -// cannot be matched to any known QuicSession. In such cases, the -// receiver may choose to send a subtle opaque indication to the -// sending peer that state for the QuicSession has apparently been -// lost. For any on- or off- path attacker, a stateless reset packet -// resembles any other QUIC packet with a short header. In order to -// be successfully handled as a stateless reset, the peer must have -// already seen a reset token issued to it associated with the given -// CID. The token itself is opaque to the peer that receives is but -// must be possible to statelessly recreate by the peer that -// originally created it. The actual implementation is Node.js -// specific but we currently defer to a utility function provided -// by ngtcp2. -bool GenerateResetToken( - uint8_t* token, - const uint8_t* secret, - const QuicCID& cid) { - ngtcp2_crypto_ctx ctx; - ngtcp2_crypto_ctx_initial(&ctx); - return NGTCP2_OK(ngtcp2_crypto_generate_stateless_reset_token( - token, - &ctx.md, - secret, - NGTCP2_STATELESS_RESET_TOKENLEN, - cid.cid())); -} - -// Generates a RETRY packet. See the notes for GenerateRetryToken for details. -std::unique_ptr<QuicPacket> GenerateRetryPacket( - const uint8_t* token_secret, - const QuicCID& dcid, - const QuicCID& scid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr) { - - uint8_t token[256]; - size_t tokenlen = sizeof(token); - - if (!GenerateRetryToken(token, &tokenlen, remote_addr, dcid, token_secret)) - return {}; - - QuicCID cid; - EntropySource(cid.data(), kScidLen); - cid.set_length(kScidLen); - - size_t pktlen = tokenlen + (2 * NGTCP2_MAX_CIDLEN) + scid.length() + 8; - - auto packet = QuicPacket::Create("retry", pktlen); - ssize_t nwrite = - ngtcp2_crypto_write_retry( - packet->data(), - NGTCP2_MAX_PKTLEN_IPV4, - scid.cid(), - cid.cid(), - dcid.cid(), - token, - tokenlen); - if (nwrite <= 0) - return {}; - packet->set_length(nwrite); - return packet; -} - -// Validates a retry token included in the given header. This will return -// true if the token cannot be validated, false otherwise. A token is -// valid if it can be successfully decrypted using the key derived from -// random data embedded in the token, the structure of the token matches -// that generated by the GenerateRetryToken function, and the token was -// not generated earlier than now - verification_expiration. If validation -// is successful, ocid will be updated to the original connection ID encoded -// in the encrypted token. -bool InvalidRetryToken( - const ngtcp2_vec& token, - const SocketAddress& addr, - QuicCID* ocid, - const uint8_t* token_secret, - uint64_t verification_expiration) { - - if (token.len < kTokenRandLen) - return true; - - ngtcp2_crypto_ctx ctx; - ngtcp2_crypto_ctx_initial(&ctx); - - size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead); - - size_t ciphertextlen = token.len - kTokenRandLen; - const uint8_t* ciphertext = token.base; - const uint8_t* rand_data = token.base + ciphertextlen; - - uint8_t token_key[kCryptoTokenKeylen]; - uint8_t token_iv[kCryptoTokenIvlen]; - - if (!DeriveTokenKey( - token_key, - token_iv, - rand_data, - kTokenRandLen, - ctx, - token_secret)) { - return true; - } - - uint8_t plaintext[4096]; - - ngtcp2_crypto_aead_ctx aead_ctx; - if (NGTCP2_ERR(ngtcp2_crypto_aead_ctx_decrypt_init( - &aead_ctx, - &ctx.aead, - token_key, - ivlen))) { - return true; - } - - if (NGTCP2_ERR(ngtcp2_crypto_decrypt( - plaintext, - &ctx.aead, - &aead_ctx, - ciphertext, - ciphertextlen, - token_iv, - ivlen, - addr.raw(), - addr.length()))) { - return true; - } - - size_t plaintextlen = ciphertextlen - ngtcp2_crypto_aead_taglen(&ctx.aead); - if (plaintextlen < addr.length() + sizeof(uint64_t)) - return true; - - ssize_t cil = plaintextlen - addr.length() - sizeof(uint64_t); - if ((cil != 0 && (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN)) || - memcmp(plaintext, addr.raw(), addr.length()) != 0) { - return true; - } - - uint64_t t; - memcpy(&t, plaintext + addr.length(), sizeof(uint64_t)); - - // 10-second window by default, but configurable for each - // QuicSocket instance with a MIN_RETRYTOKEN_EXPIRATION second - // minimum and a MAX_RETRYTOKEN_EXPIRATION second maximum. - if (t + verification_expiration * NGTCP2_SECONDS < uv_hrtime()) - return true; - - ngtcp2_cid_init( - ocid->cid(), - plaintext + addr.length() + sizeof(uint64_t), - cil); - - return false; -} - -// Get the ALPN protocol identifier that was negotiated for the session -Local<Value> GetALPNProtocol(const QuicSession& session) { - QuicCryptoContext* ctx = session.crypto_context(); - Environment* env = session.env(); - std::string alpn = ctx->selected_alpn(); - // This supposed to be `NGHTTP3_ALPN_H3 + 1` - // Details see https://github.com/nodejs/node/issues/33959 - if (alpn == &NGHTTP3_ALPN_H3[1]) { - return env->http3_alpn_string(); - } else { - return ToV8Value( - env->context(), - alpn, - env->isolate()).FromMaybe(Local<Value>()); - } -} - -namespace { -int CertCB(SSL* ssl, void* arg) { - QuicSession* session = static_cast<QuicSession*>(arg); - int ret; - switch (SSL_get_tlsext_status_type(ssl)) { - case TLSEXT_STATUSTYPE_ocsp: - ret = session->crypto_context()->OnOCSP(); - return UNLIKELY(session->is_destroyed()) ? 0 : ret; - default: - return 1; - } -} - -void Keylog_CB(const SSL* ssl, const char* line) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - session->crypto_context()->Keylog(line); -} - -int Client_Hello_CB( - SSL* ssl, - int* tls_alert, - void* arg) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - int ret = session->crypto_context()->OnClientHello(); - if (UNLIKELY(session->is_destroyed())) { - *tls_alert = SSL_R_SSL_HANDSHAKE_FAILURE; - return 0; - } - switch (ret) { - case 0: - return 1; - case -1: - return -1; - default: - *tls_alert = ret; - return 0; - } -} - -int AlpnSelection( - SSL* ssl, - const unsigned char** out, - unsigned char* outlen, - const unsigned char* in, - unsigned int inlen, - void* arg) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - - unsigned char* tmp; - - // The QuicServerSession supports exactly one ALPN identifier. If that does - // not match any of the ALPN identifiers provided in the client request, - // then we fail here. Note that this will not fail the TLS handshake, so - // we have to check later if the ALPN matches the expected identifier or not. - if (SSL_select_next_proto( - &tmp, - outlen, - reinterpret_cast<const unsigned char*>(session->alpn().c_str()), - session->alpn().length(), - in, - inlen) == OPENSSL_NPN_NO_OVERLAP) { - return SSL_TLSEXT_ERR_NOACK; - } - *out = tmp; - return SSL_TLSEXT_ERR_OK; -} - -int AllowEarlyDataCB(SSL* ssl, void* arg) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - return session->allow_early_data() ? 1 : 0; -} - -int TLS_Status_Callback(SSL* ssl, void* arg) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - return session->crypto_context()->OnTLSStatus(); -} - -int New_Session_Callback(SSL* ssl, SSL_SESSION* session) { - QuicSession* s = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - return s->set_session(session); -} - -int GenerateSessionTicket(SSL* ssl, void* arg) { - QuicSession* s = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - SessionTicketAppData app_data(SSL_get_session(ssl)); - s->SetSessionTicketAppData(app_data); - return 1; -} - -SSL_TICKET_RETURN DecryptSessionTicket( - SSL* ssl, - SSL_SESSION* session, - const unsigned char* keyname, - size_t keyname_len, - SSL_TICKET_STATUS status, - void* arg) { - QuicSession* s = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - SessionTicketAppData::Flag flag = SessionTicketAppData::Flag::STATUS_NONE; - switch (status) { - default: - return SSL_TICKET_RETURN_IGNORE; - case SSL_TICKET_EMPTY: - // Fall through - case SSL_TICKET_NO_DECRYPT: - return SSL_TICKET_RETURN_IGNORE_RENEW; - case SSL_TICKET_SUCCESS_RENEW: - flag = SessionTicketAppData::Flag::STATUS_RENEW; - // Fall through - case SSL_TICKET_SUCCESS: - SessionTicketAppData app_data(session); - switch (s->GetSessionTicketAppData(app_data, flag)) { - default: - return SSL_TICKET_RETURN_IGNORE; - case SessionTicketAppData::Status::TICKET_IGNORE: - return SSL_TICKET_RETURN_IGNORE; - case SessionTicketAppData::Status::TICKET_IGNORE_RENEW: - return SSL_TICKET_RETURN_IGNORE_RENEW; - case SessionTicketAppData::Status::TICKET_USE: - return SSL_TICKET_RETURN_USE; - case SessionTicketAppData::Status::TICKET_USE_RENEW: - return SSL_TICKET_RETURN_USE_RENEW; - } - } -} - -int SetEncryptionSecrets( - SSL* ssl, - OSSL_ENCRYPTION_LEVEL ossl_level, - const uint8_t* read_secret, - const uint8_t* write_secret, - size_t secret_len) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - return session->crypto_context()->OnSecrets( - from_ossl_level(ossl_level), - read_secret, - write_secret, - secret_len) ? 1 : 0; -} - -int AddHandshakeData( - SSL* ssl, - OSSL_ENCRYPTION_LEVEL ossl_level, - const uint8_t* data, - size_t len) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - session->crypto_context()->WriteHandshake( - from_ossl_level(ossl_level), - data, - len); - return 1; -} - -int FlushFlight(SSL* ssl) { return 1; } - -int SendAlert( - SSL* ssl, - enum ssl_encryption_level_t level, - uint8_t alert) { - QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl)); - session->crypto_context()->set_tls_alert(alert); - return 1; -} - -bool SetTransportParams(QuicSession* session, const crypto::SSLPointer& ssl) { - ngtcp2_transport_params params; - ngtcp2_conn_get_local_transport_params(session->connection(), ¶ms); - uint8_t buf[512]; - ssize_t nwrite = ngtcp2_encode_transport_params( - buf, - arraysize(buf), - NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, - ¶ms); - return nwrite >= 0 && - SSL_set_quic_transport_params(ssl.get(), buf, nwrite) == 1; -} - -SSL_QUIC_METHOD quic_method = SSL_QUIC_METHOD{ - SetEncryptionSecrets, - AddHandshakeData, - FlushFlight, - SendAlert -}; - -void SetHostname(const crypto::SSLPointer& ssl, const std::string& hostname) { - // If the hostname is an IP address, use an empty string - // as the hostname instead. - X509_VERIFY_PARAM* param = SSL_get0_param(ssl.get()); - X509_VERIFY_PARAM_set_hostflags(param, 0); - - if (UNLIKELY(SocketAddress::is_numeric_host(hostname.c_str()))) { - SSL_set_tlsext_host_name(ssl.get(), ""); - CHECK_EQ(X509_VERIFY_PARAM_set1_host(param, "", 0), 1); - } else { - SSL_set_tlsext_host_name(ssl.get(), hostname.c_str()); - CHECK_EQ( - X509_VERIFY_PARAM_set1_host(param, hostname.c_str(), hostname.length()), - 1); - } -} - -} // namespace - -void InitializeTLS(QuicSession* session, const crypto::SSLPointer& ssl) { - QuicCryptoContext* ctx = session->crypto_context(); - Environment* env = session->env(); - QuicState* quic_state = session->quic_state(); - - SSL_set_app_data(ssl.get(), session); - SSL_set_cert_cb(ssl.get(), CertCB, - const_cast<void*>(reinterpret_cast<const void*>(session))); - SSL_set_verify(ssl.get(), SSL_VERIFY_NONE, crypto::VerifyCallback); - - // Enable tracing if the `--trace-tls` command line flag is used. - if (env->options()->trace_tls) { - ctx->EnableTrace(); - if (quic_state->warn_trace_tls) { - quic_state->warn_trace_tls = false; - ProcessEmitWarning(env, - "Enabling --trace-tls can expose sensitive data " - "in the resulting log"); - } - } - - switch (ctx->side()) { - case NGTCP2_CRYPTO_SIDE_CLIENT: { - SSL_set_connect_state(ssl.get()); - crypto::SetALPN(ssl, session->alpn()); - SetHostname(ssl, session->hostname()); - if (ctx->is_option_set(QUICCLIENTSESSION_OPTION_REQUEST_OCSP)) - SSL_set_tlsext_status_type(ssl.get(), TLSEXT_STATUSTYPE_ocsp); - break; - } - case NGTCP2_CRYPTO_SIDE_SERVER: { - SSL_set_accept_state(ssl.get()); - if (ctx->is_option_set(QUICSERVERSESSION_OPTION_REQUEST_CERT)) { - int verify_mode = SSL_VERIFY_PEER; - if (ctx->is_option_set(QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED)) - verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - SSL_set_verify(ssl.get(), verify_mode, crypto::VerifyCallback); - } - break; - } - default: - UNREACHABLE(); - } - - ngtcp2_conn_set_tls_native_handle(session->connection(), ssl.get()); - SetTransportParams(session, ssl); -} - -void InitializeSecureContext( - BaseObjectPtr<crypto::SecureContext> sc, - bool early_data, - ngtcp2_crypto_side side) { - constexpr static unsigned char session_id_ctx[] = "node.js quic server"; - switch (side) { - case NGTCP2_CRYPTO_SIDE_SERVER: - SSL_CTX_set_options( - **sc, - (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | - SSL_OP_SINGLE_ECDH_USE | - SSL_OP_CIPHER_SERVER_PREFERENCE | - SSL_OP_NO_ANTI_REPLAY); - - SSL_CTX_set_mode(**sc, SSL_MODE_RELEASE_BUFFERS); - - SSL_CTX_set_alpn_select_cb(**sc, AlpnSelection, nullptr); - SSL_CTX_set_client_hello_cb(**sc, Client_Hello_CB, nullptr); - - SSL_CTX_set_session_ticket_cb( - **sc, - GenerateSessionTicket, - DecryptSessionTicket, - nullptr); - - if (early_data) { - SSL_CTX_set_max_early_data(**sc, 0xffffffff); - SSL_CTX_set_allow_early_data_cb(**sc, AllowEarlyDataCB, nullptr); - } - - SSL_CTX_set_session_id_context( - **sc, - session_id_ctx, - sizeof(session_id_ctx) - 1); - break; - case NGTCP2_CRYPTO_SIDE_CLIENT: - SSL_CTX_set_session_cache_mode( - **sc, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL_STORE); - SSL_CTX_sess_set_new_cb(**sc, New_Session_Callback); - break; - default: - UNREACHABLE(); - } - SSL_CTX_set_min_proto_version(**sc, TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(**sc, TLS1_3_VERSION); - SSL_CTX_set_default_verify_paths(**sc); - SSL_CTX_set_tlsext_status_cb(**sc, TLS_Status_Callback); - SSL_CTX_set_keylog_callback(**sc, Keylog_CB); - SSL_CTX_set_tlsext_status_arg(**sc, nullptr); - SSL_CTX_set_quic_method(**sc, &quic_method); -} - -ngtcp2_crypto_level from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) { - switch (ossl_level) { - case ssl_encryption_initial: - return NGTCP2_CRYPTO_LEVEL_INITIAL; - case ssl_encryption_early_data: - return NGTCP2_CRYPTO_LEVEL_EARLY; - case ssl_encryption_handshake: - return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; - case ssl_encryption_application: - return NGTCP2_CRYPTO_LEVEL_APP; - default: - UNREACHABLE(); - } -} - -const char* crypto_level_name(ngtcp2_crypto_level level) { - switch (level) { - case NGTCP2_CRYPTO_LEVEL_INITIAL: - return "initial"; - case NGTCP2_CRYPTO_LEVEL_EARLY: - return "early"; - case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - return "handshake"; - case NGTCP2_CRYPTO_LEVEL_APP: - return "app"; - default: - UNREACHABLE(); - } -} - -// When using IPv6, QUIC recommends the use of IPv6 Flow Labels -// as specified in https://tools.ietf.org/html/rfc6437. These -// are used as a means of reliably associating packets exchanged -// as part of a single flow and protecting against certain kinds -// of attacks. -uint32_t GenerateFlowLabel( - const SocketAddress& local, - const SocketAddress& remote, - const QuicCID& cid, - const uint8_t* secret, - size_t secretlen) { - static constexpr size_t kInfoLen = - (sizeof(sockaddr_in6) * 2) + NGTCP2_MAX_CIDLEN; - - uint32_t label = 0; - - std::array<uint8_t, kInfoLen> plaintext; - size_t infolen = local.length() + remote.length() + cid.length(); - CHECK_LE(infolen, kInfoLen); - - ngtcp2_crypto_ctx ctx; - ngtcp2_crypto_ctx_initial(&ctx); - - auto p = std::begin(plaintext); - p = std::copy_n(local.raw(), local.length(), p); - p = std::copy_n(remote.raw(), remote.length(), p); - p = std::copy_n(cid->data, cid->datalen, p); - - ngtcp2_crypto_hkdf_expand( - reinterpret_cast<uint8_t*>(&label), - sizeof(label), - &ctx.md, - secret, - secretlen, - plaintext.data(), - infolen); - - label &= kLabelMask; - DCHECK_LE(label, kLabelMask); - return label; -} - -} // namespace quic -} // namespace node diff --git a/src/quic/node_quic_crypto.h b/src/quic/node_quic_crypto.h deleted file mode 100644 index e37c5e059a5..00000000000 --- a/src/quic/node_quic_crypto.h +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_CRYPTO_H_ -#define SRC_QUIC_NODE_QUIC_CRYPTO_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "node_crypto.h" -#include "node_quic_util.h" -#include "v8.h" - -#include <ngtcp2/ngtcp2.h> -#include <ngtcp2/ngtcp2_crypto.h> -#include <openssl/ssl.h> - -namespace node { - -namespace quic { - -// Crypto and OpenSSL related utility functions used in -// various places throughout the QUIC implementation. - -// Forward declaration -class QuicSession; -class QuicPacket; - -// many ngtcp2 functions return 0 to indicate success -// and non-zero to indicate failure. Most of the time, -// for such functions we don't care about the specific -// return value so we simplify using a macro. - -#define NGTCP2_ERR(V) (V != 0) -#define NGTCP2_OK(V) (V == 0) - -// Called by QuicInitSecureContext to initialize the -// given SecureContext with the defaults for the given -// QUIC side (client or server). -void InitializeSecureContext( - BaseObjectPtr<crypto::SecureContext> sc, - bool early_data, - ngtcp2_crypto_side side); - -// Called in the QuicSession::InitServer and -// QuicSession::InitClient to configure the -// appropriate settings for the SSL* associated -// with the session. -void InitializeTLS(QuicSession* session, const crypto::SSLPointer& ssl); - -// Generates a stateless reset token using HKDF with the -// cid and token secret as input. The token secret is -// either provided by user code when a QuicSocket is -// created or is generated randomly. -// -// QUIC leaves the generation of stateless session tokens -// up to the implementation to figure out. The idea, however, -// is that it ought to be possible to generate a stateless -// reset token reliably even when all state for a connection -// has been lost. We use the cid as it's the only reliably -// consistent bit of data we have when a session is destroyed. -bool GenerateResetToken( - uint8_t* token, - const uint8_t* secret, - const QuicCID& cid); - -// The Retry Token is an encrypted token that is sent to the client -// by the server as part of the path validation flow. The plaintext -// format within the token is opaque and only meaningful the server. -// We can structure it any way we want. It needs to: -// * be hard to guess -// * be time limited -// * be specific to the client address -// * be specific to the original cid -// * contain random data. -std::unique_ptr<QuicPacket> GenerateRetryPacket( - const uint8_t* token_secret, - const QuicCID& dcid, - const QuicCID& scid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr); - -// The IPv6 Flow Label is generated and set whenever IPv6 is used. -// The label is derived as a cryptographic function of the CID, -// local and remote addresses, and the given secret, that is then -// truncated to a 20-bit value (per IPv6 requirements). In QUIC, -// the flow label *may* be used as a way of disambiguating IP -// packets that belong to the same flow from a remote peer. -uint32_t GenerateFlowLabel( - const SocketAddress& local, - const SocketAddress& remote, - const QuicCID& cid, - const uint8_t* secret, - size_t secretlen); - -// Verifies the validity of a retry token. Returns true if the -// token is *not valid*, false otherwise. If the token is valid, -// the ocid will be updated to the original CID value encoded -// within the successfully validated, encrypted token. -bool InvalidRetryToken( - const ngtcp2_vec& token, - const SocketAddress& addr, - QuicCID* ocid, - const uint8_t* token_secret, - uint64_t verification_expiration); - -// Get the ALPN protocol identifier that was negotiated for the session -v8::Local<v8::Value> GetALPNProtocol(const QuicSession& session); - -ngtcp2_crypto_level from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level); -const char* crypto_level_name(ngtcp2_crypto_level level); - -// SessionTicketAppData is a utility class that is used only during -// the generation or access of TLS stateless sesson tickets. It -// exists solely to provide a easier way for QuicApplication instances -// to set relevant metadata in the session ticket when it is created, -// and the exract and subsequently verify that data when a ticket is -// received and is being validated. The app data is completely opaque -// to anything other than the server-side of the QuicApplication that -// sets it. -class SessionTicketAppData { - public: - enum class Status { - TICKET_USE, - TICKET_USE_RENEW, - TICKET_IGNORE, - TICKET_IGNORE_RENEW - }; - - enum class Flag { - STATUS_NONE, - STATUS_RENEW - }; - - explicit SessionTicketAppData(SSL_SESSION* session) : session_(session) {} - bool Set(const uint8_t* data, size_t len); - bool Get(uint8_t** data, size_t* len) const; - - private: - bool set_ = false; - SSL_SESSION* session_; -}; - -} // namespace quic -} // namespace node - -#endif // NODE_WANT_INTERNALS -#endif // SRC_QUIC_NODE_QUIC_CRYPTO_H_ diff --git a/src/quic/node_quic_default_application.cc b/src/quic/node_quic_default_application.cc deleted file mode 100644 index 5005d3d6919..00000000000 --- a/src/quic/node_quic_default_application.cc +++ /dev/null @@ -1,177 +0,0 @@ -#include "debug_utils-inl.h" -#include "node_quic_buffer-inl.h" -#include "node_quic_default_application.h" -#include "node_quic_session-inl.h" -#include "node_quic_socket-inl.h" -#include "node_quic_stream-inl.h" -#include "node_quic_util-inl.h" -#include "node_sockaddr-inl.h" -#include <ngtcp2/ngtcp2.h> - -#include <vector> - -namespace node { -namespace quic { - -namespace { -void Consume(ngtcp2_vec** pvec, size_t* pcnt, size_t len) { - ngtcp2_vec* v = *pvec; - size_t cnt = *pcnt; - - for (; cnt > 0; --cnt, ++v) { - if (v->len > len) { - v->len -= len; - v->base += len; - break; - } - len -= v->len; - } - - *pvec = v; - *pcnt = cnt; -} - -int IsEmpty(const ngtcp2_vec* vec, size_t cnt) { - size_t i; - for (i = 0; i < cnt && vec[i].len == 0; ++i) {} - return i == cnt; -} -} // anonymous namespace - -DefaultApplication::DefaultApplication( - QuicSession* session) - : QuicApplication(session) {} - -bool DefaultApplication::Initialize() { - if (needs_init()) { - Debug(session(), "Default QUIC Application Initialized"); - set_init_done(); - } - return true; -} - -void DefaultApplication::ScheduleStream(int64_t stream_id) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - if (LIKELY(stream && !stream->is_destroyed())) { - Debug(session(), "Scheduling stream %" PRIu64, stream_id); - stream->Schedule(&stream_queue_); - } -} - -void DefaultApplication::UnscheduleStream(int64_t stream_id) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - if (LIKELY(stream)) { - Debug(session(), "Unscheduling stream %" PRIu64, stream_id); - stream->Unschedule(); - } -} - -void DefaultApplication::ResumeStream(int64_t stream_id) { - ScheduleStream(stream_id); -} - -bool DefaultApplication::ReceiveStreamData( - uint32_t flags, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - uint64_t offset) { - // Ensure that the QuicStream exists before deferring to - // QuicApplication specific processing logic. - Debug(session(), "Default QUIC Application receiving stream data"); - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - if (!stream) { - // Shutdown the stream explicitly if the session is being closed. - if (session()->is_graceful_closing()) { - session()->ShutdownStream(stream_id, NGTCP2_ERR_CLOSING); - return true; - } - - // One potential DOS attack vector is to send a bunch of - // empty stream frames to commit resources. Check that - // here. Essentially, we only want to create a new stream - // if the datalen is greater than 0, otherwise, we ignore - // the packet. ngtcp2 should be handling this for us, - // but we handle it just to be safe. - if (UNLIKELY(datalen == 0)) - return true; - - stream = session()->CreateStream(stream_id); - } - CHECK(stream); - - // If the stream ended up being destroyed immediately after - // creation, just skip the data processing and return. - if (UNLIKELY(stream->is_destroyed())) - return true; - - stream->ReceiveData(flags, data, datalen, offset); - return true; -} - -int DefaultApplication::GetStreamData(StreamData* stream_data) { - QuicStream* stream = stream_queue_.PopFront(); - // If stream is nullptr, there are no streams with data pending. - if (stream == nullptr) - return 0; - - stream_data->stream.reset(stream); - stream_data->id = stream->id(); - - auto next = [&]( - int status, - const ngtcp2_vec* data, - size_t count, - bob::Done done) { - switch (status) { - case bob::Status::STATUS_BLOCK: - // Fall through - case bob::Status::STATUS_WAIT: - // Fall through - case bob::Status::STATUS_EOS: - return; - case bob::Status::STATUS_END: - stream_data->fin = 1; - } - - stream_data->count = count; - - if (count > 0) { - stream->Schedule(&stream_queue_); - stream_data->remaining = get_length(data, count); - } else { - stream_data->remaining = 0; - } - }; - - if (LIKELY(!stream->is_eos())) { - CHECK_GE(stream->Pull( - std::move(next), - bob::Options::OPTIONS_SYNC, - stream_data->data, - arraysize(stream_data->data), - kMaxVectorCount), 0); - } - - return 0; -} - -bool DefaultApplication::StreamCommit( - StreamData* stream_data, - size_t datalen) { - CHECK(stream_data->stream); - stream_data->remaining -= datalen; - Consume(&stream_data->buf, &stream_data->count, datalen); - stream_data->stream->Commit(datalen); - return true; -} - -bool DefaultApplication::ShouldSetFin(const StreamData& stream_data) { - if (!stream_data.stream || - !IsEmpty(stream_data.buf, stream_data.count)) - return false; - return !stream_data.stream->is_writable(); -} - -} // namespace quic -} // namespace node diff --git a/src/quic/node_quic_default_application.h b/src/quic/node_quic_default_application.h deleted file mode 100644 index 52c9044c0e3..00000000000 --- a/src/quic/node_quic_default_application.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_DEFAULT_APPLICATION_H_ -#define SRC_QUIC_NODE_QUIC_DEFAULT_APPLICATION_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "node_quic_stream.h" -#include "node_quic_session.h" -#include "node_quic_util.h" -#include "util.h" -#include "v8.h" - -namespace node { - -namespace quic { - -// The DefaultApplication is used whenever an unknown/unrecognized -// alpn identifier is used. It switches the QUIC implementation into -// a minimal/generic mode that defers all application level processing -// to the user-code level. Headers are not supported by QuicStream -// instances created under the default application. -class DefaultApplication final : public QuicApplication { - public: - explicit DefaultApplication(QuicSession* session); - - bool Initialize() override; - - void StopTrackingMemory(void* ptr) override { - // Do nothing. Not used. - } - - bool ReceiveStreamData( - uint32_t flags, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - uint64_t offset) override; - - int GetStreamData(StreamData* stream_data) override; - - void ResumeStream(int64_t stream_id) override; - bool ShouldSetFin(const StreamData& stream_data) override; - bool StreamCommit(StreamData* stream_data, size_t datalen) override; - - SET_SELF_SIZE(DefaultApplication) - SET_MEMORY_INFO_NAME(DefaultApplication) - SET_NO_MEMORY_INFO() - - private: - void ScheduleStream(int64_t stream_id); - void UnscheduleStream(int64_t stream_id); - - QuicStream::Queue stream_queue_; -}; - -} // namespace quic - -} // namespace node - -#endif // NODE_WANT_INTERNALS -#endif // SRC_QUIC_NODE_QUIC_DEFAULT_APPLICATION_H_ diff --git a/src/quic/node_quic_http3_application.cc b/src/quic/node_quic_http3_application.cc deleted file mode 100644 index 82cd677d1ce..00000000000 --- a/src/quic/node_quic_http3_application.cc +++ /dev/null @@ -1,941 +0,0 @@ -#include "node.h" -#include "debug_utils-inl.h" -#include "node_mem-inl.h" -#include "node_quic_buffer-inl.h" -#include "node_quic_http3_application.h" -#include "node_quic_session-inl.h" -#include "node_quic_socket-inl.h" -#include "node_quic_stream-inl.h" -#include "node_quic_util-inl.h" -#include "node_sockaddr-inl.h" -#include "node_http_common-inl.h" - -#include <ngtcp2/ngtcp2.h> -#include <nghttp3/nghttp3.h> -#include <algorithm> - -namespace node { - -using v8::Array; -using v8::Local; - -namespace quic { - -// nghttp3 uses a numeric identifier for a large number -// of known HTTP header names. These allow us to use -// static strings for those rather than allocating new -// strings all of the time. The list of strings supported -// is included in node_http_common.h -#define V1(name, value) case NGHTTP3_QPACK_TOKEN__##name: return value; -#define V2(name, value) case NGHTTP3_QPACK_TOKEN_##name: return value; -const char* Http3HeaderTraits::ToHttpHeaderName(int32_t token) { - switch (token) { - default: - // Fall through - case -1: return nullptr; - HTTP_SPECIAL_HEADERS(V1) - HTTP_REGULAR_HEADERS(V2) - } -} -#undef V1 -#undef V2 - -template <typename M, typename T> -void Http3Application::SetConfig( - int idx, - M T::*member) { - AliasedFloat64Array& buffer = session()->quic_state()->http3config_buffer; - uint64_t flags = static_cast<uint64_t>(buffer[IDX_HTTP3_CONFIG_COUNT]); - if (flags & (1ULL << idx)) - config_.*member = static_cast<uint64_t>(buffer[idx]); -} - -Http3Application::Http3Application( - QuicSession* session) - : QuicApplication(session), - alloc_info_(MakeAllocator()) { - // Collect Configuration Details. - SetConfig<size_t>(IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, - &Http3ApplicationConfig::qpack_max_table_capacity); - SetConfig<size_t>(IDX_HTTP3_QPACK_BLOCKED_STREAMS, - &Http3ApplicationConfig::qpack_blocked_streams); - SetConfig(IDX_HTTP3_MAX_HEADER_LIST_SIZE, - &Http3ApplicationConfig::max_field_section_size); - SetConfig(IDX_HTTP3_MAX_PUSHES, - &Http3ApplicationConfig::max_pushes); - SetConfig(IDX_HTTP3_MAX_HEADER_PAIRS, - &Http3ApplicationConfig::max_header_pairs); - SetConfig(IDX_HTTP3_MAX_HEADER_LENGTH, - &Http3ApplicationConfig::max_header_length); - set_max_header_pairs( - session->is_server() - ? GetServerMaxHeaderPairs(config_.max_header_pairs) - : GetClientMaxHeaderPairs(config_.max_header_pairs)); - set_max_header_length(config_.max_header_length); - - session->quic_state()->http3config_buffer[IDX_HTTP3_CONFIG_COUNT] = 0; -} - -// Push streams in HTTP/3 are a bit complicated. -// First, it's important to know that only an HTTP/3 server can -// create a push stream. -// Second, a push stream is essentially an *assumed* request. For -// instance, if a client requests a webpage that has links to css -// and js files, and the server expects the client to send subsequent -// requests for those css and js files, the server can shortcut the -// process by opening a push stream for each additional resource -// it assumes the client to make. -// Third, a push stream can only be opened within the context -// of an HTTP/3 request/response. Essentially, a server receives -// a request and while processing the response, the server can -// open one or more push streams. -// -// Now... a push stream consists of two components: a push promise -// and a push fulfillment. The push promise is sent *as part of -// the response on the original stream* and is assigned a push id -// and a block of headers containing the *assumed request headers*. -// The push promise is sent on the request/response bidirectional -// stream. -// The push fulfillment is a unidirectional stream opened by the -// server that contains the push id, the response header block, and -// the response payload. -// Here's where it can get a bit complicated: the server sends the -// push promise and the push fulfillment on two different, and -// independent QUIC streams. The push id is used to correlate -// those on the client side, but, it's entirely possible for the -// client to receive the push fulfillment before it actually receives -// the push promise. It's *unlikely*, but it's possible. Fortunately, -// nghttp3 handles the complexity of that for us internally but -// makes for some weird timing and could lead to some amount of -// buffering to occur. -// -// The *logical* order of events from the client side *should* -// be: (a) receive the push promise containing assumed request -// headers, (b) receive the push fulfillment containing the -// response headers followed immediately by the response payload. -// -// On the server side, the steps are: (a) first create the push -// promise creating the push_id then (b) open the unidirectional -// stream that will be used to fullfil the push promise. Once that -// unidirectional stream is created, the push id and unidirectional -// stream ID must be bound. The CreateAndBindPushStream handles (b) -int64_t Http3Application::CreateAndBindPushStream(int64_t push_id) { - CHECK(session()->is_server()); - int64_t stream_id; - if (!session()->OpenUnidirectionalStream(&stream_id)) - return 0; - return nghttp3_conn_bind_push_stream( - connection(), - push_id, - stream_id) == 0 ? stream_id : 0; -} - -bool Http3Application::SubmitPushPromise( - int64_t id, - int64_t* push_id, - int64_t* stream_id, - const Http3Headers& headers) { - // Successfully creating the push promise and opening the - // fulfillment stream will queue nghttp3 up to send data. - // Creating the SendSessionScope here ensures that when - // SubmitPush exits, SendPendingData will be called if - // we are not within the context of an ngtcp2 callback. - QuicSession::SendSessionScope send_scope(session()); - - Debug( - session(), - "Submitting %d push promise headers", - headers.length()); - if (nghttp3_conn_submit_push_promise( - connection(), - push_id, - id, - headers.data(), - headers.length()) != 0) { - return false; - } - // Once we've successfully submitting the push promise and have - // a push id assigned, we create the push fulfillment stream. - *stream_id = CreateAndBindPushStream(*push_id); - return *stream_id != 0; // push stream can never use stream id 0 -} - -bool Http3Application::SubmitInformation( - int64_t id, - const Http3Headers& headers) { - QuicSession::SendSessionScope send_scope(session()); - Debug( - session(), - "Submitting %d informational headers for stream %" PRId64, - headers.length(), - id); - return nghttp3_conn_submit_info( - connection(), - id, - headers.data(), - headers.length()) == 0; -} - -bool Http3Application::SubmitTrailers( - int64_t id, - const Http3Headers& headers) { - QuicSession::SendSessionScope send_scope(session()); - Debug( - session(), - "Submitting %d trailing headers for stream %" PRId64, - headers.length(), - id); - return nghttp3_conn_submit_trailers( - connection(), - id, - headers.data(), - headers.length()) == 0; -} - -bool Http3Application::SubmitHeaders( - int64_t id, - const Http3Headers& headers, - int32_t flags) { - QuicSession::SendSessionScope send_scope(session()); - static constexpr nghttp3_data_reader reader = { - Http3Application::OnReadData }; - const nghttp3_data_reader* reader_ptr = nullptr; - if (!(flags & QUICSTREAM_HEADER_FLAGS_TERMINAL)) - reader_ptr = &reader; - - switch (session()->crypto_context()->side()) { - case NGTCP2_CRYPTO_SIDE_CLIENT: - return nghttp3_conn_submit_request( - connection(), - id, - headers.data(), - headers.length(), - reader_ptr, - nullptr) == 0; - case NGTCP2_CRYPTO_SIDE_SERVER: - return nghttp3_conn_submit_response( - connection(), - id, - headers.data(), - headers.length(), - reader_ptr) == 0; - default: - UNREACHABLE(); - } -} - -// SubmitPush initiates a push stream by first creating a push promise -// with an associated push id, then opening the unidirectional stream -// that is used to fullfill it. Assuming both operations are successful, -// the QuicStream instance is created and added to the server QuicSession. -// -// The headers block passed to the submit push contains the assumed -// *request* headers. The response headers are provided using the -// SubmitHeaders() function on the created QuicStream. -BaseObjectPtr<QuicStream> Http3Application::SubmitPush( - int64_t id, - Local<Array> headers) { - // If the QuicSession is not a server session, return false - // immediately. Push streams cannot be sent by an HTTP/3 client. - if (!session()->is_server()) - return {}; - - Http3Headers nva(env(), headers); - int64_t push_id; - int64_t stream_id; - - // There are several reasons why push may fail. We currently handle - // them all the same. Later we might want to differentiate when the - // return value is NGHTTP3_ERR_PUSH_ID_BLOCKED. - return SubmitPushPromise(id, &push_id, &stream_id, nva) ? - QuicStream::New(session(), stream_id, push_id) : - BaseObjectPtr<QuicStream>(); -} - -// Submit informational headers (response headers that use a 1xx -// status code). If the QuicSession is not a server session, return -// false immediately because info headers cannot be sent by a -// client -bool Http3Application::SubmitInformation( - int64_t stream_id, - Local<Array> headers) { - if (!session()->is_server()) - return false; - Http3Headers nva(session()->env(), headers); - return SubmitInformation(stream_id, nva); -} - -// For client sessions, submits request headers. For server sessions, -// submits response headers. -bool Http3Application::SubmitHeaders( - int64_t stream_id, - Local<Array> headers, - uint32_t flags) { - Http3Headers nva(session()->env(), headers); - return SubmitHeaders(stream_id, nva, flags); -} - -// Submits trailing headers for the HTTP/3 request or response. -bool Http3Application::SubmitTrailers( - int64_t stream_id, - Local<Array> headers) { - Http3Headers nva(session()->env(), headers); - return SubmitTrailers(stream_id, nva); -} - -void Http3Application::CheckAllocatedSize(size_t previous_size) const { - CHECK_GE(current_nghttp3_memory_, previous_size); -} - -void Http3Application::IncreaseAllocatedSize(size_t size) { - current_nghttp3_memory_ += size; -} - -void Http3Application::DecreaseAllocatedSize(size_t size) { - current_nghttp3_memory_ -= size; -} - -void Http3Application::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackFieldWithSize("current_nghttp3_memory", - current_nghttp3_memory_); -} - -// Creates the underlying nghttp3 connection state for the session. -void Http3Application::CreateConnection() { - // nghttp3_conn_server_new and nghttp3_conn_client_new share - // identical definitions, so new_fn will work for both. - using new_fn = decltype(&nghttp3_conn_server_new); - static new_fn fns[] = { - nghttp3_conn_client_new, // NGTCP2_CRYPTO_SIDE_CLIENT - nghttp3_conn_server_new, // NGTCP2_CRYPTO_SIDE_SERVER - }; - - ngtcp2_crypto_side side = session()->crypto_context()->side(); - nghttp3_conn* conn; - - CHECK_EQ(fns[side]( - &conn, - &callbacks_[side], - &config_, - &alloc_info_, - this), 0); - CHECK_NOT_NULL(conn); - connection_.reset(conn); -} - -// The HTTP/3 QUIC binding uses a single unidirectional control -// stream in each direction to exchange frames impacting the entire -// connection. -bool Http3Application::CreateAndBindControlStream() { - if (!session()->OpenUnidirectionalStream(&control_stream_id_)) - return false; - Debug( - session(), - "Open stream %" PRId64 " and bind as control stream", - control_stream_id_); - return nghttp3_conn_bind_control_stream( - connection(), - control_stream_id_) == 0; -} - -// The HTTP/3 QUIC binding creates two unidirectional streams in -// each direction to exchange header compression details. -bool Http3Application::CreateAndBindQPackStreams() { - if (!session()->OpenUnidirectionalStream(&qpack_enc_stream_id_) || - !session()->OpenUnidirectionalStream(&qpack_dec_stream_id_)) { - return false; - } - Debug( - session(), - "Open streams %" PRId64 " and %" PRId64 " and bind as qpack streams", - qpack_enc_stream_id_, - qpack_dec_stream_id_); - return nghttp3_conn_bind_qpack_streams( - connection(), - qpack_enc_stream_id_, - qpack_dec_stream_id_) == 0; -} - -bool Http3Application::Initialize() { - if (!needs_init()) - return false; - - // The QuicSession must allow for at least three local unidirectional streams. - // This number is fixed by the http3 specification and represent the - // control stream and two qpack management streams. - if (session()->max_local_streams_uni() < 3) - return false; - - Debug(session(), "QPack Max Table Capacity: %" PRIu64, - config_.qpack_max_table_capacity); - Debug(session(), "QPack Blocked Streams: %" PRIu64, - config_.qpack_blocked_streams); - Debug(session(), "Max Header List Size: %" PRIu64, - config_.max_field_section_size); - Debug(session(), "Max Pushes: %" PRIu64, - config_.max_pushes); - - CreateConnection(); - Debug(session(), "HTTP/3 connection created"); - - ngtcp2_transport_params params; - session()->GetLocalTransportParams(¶ms); - - if (session()->is_server()) { - nghttp3_conn_set_max_client_streams_bidi( - connection(), - params.initial_max_streams_bidi); - } - - if (!CreateAndBindControlStream() || - !CreateAndBindQPackStreams()) { - return false; - } - - set_init_done(); - return true; -} - -// All HTTP/3 control, header, and stream data arrives as QUIC stream data. -// Here we pass the received data off to nghttp3 for processing. This will -// trigger the invocation of the various nghttp3 callbacks. -bool Http3Application::ReceiveStreamData( - uint32_t flags, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - uint64_t offset) { - Debug(session(), "Receiving %" PRIu64 " bytes for stream %" PRIu64 "%s", - datalen, - stream_id, - flags & NGTCP2_STREAM_DATA_FLAG_FIN ? " (fin)" : ""); - ssize_t nread = - nghttp3_conn_read_stream( - connection(), - stream_id, - data, - datalen, - flags & NGTCP2_STREAM_DATA_FLAG_FIN); - if (nread < 0) { - Debug(session(), "Failure to read HTTP/3 Stream Data [%" PRId64 "]", nread); - return false; - } - - return true; -} - -// This is the QUIC-level stream data acknowledgement. It is called for -// all streams, including unidirectional streams. This has to forward on -// to nghttp3 for processing. The Http3Application::AckedStreamData might -// be called as a result to acknowledge (and free) QuicStream data. -void Http3Application::AcknowledgeStreamData( - int64_t stream_id, - uint64_t offset, - size_t datalen) { - if (nghttp3_conn_add_ack_offset(connection(), stream_id, datalen) != 0) - Debug(session(), "Failure to acknowledge HTTP/3 Stream Data"); -} - -void Http3Application::StreamClose( - int64_t stream_id, - uint64_t app_error_code) { - if (app_error_code == 0) - app_error_code = NGTCP2_APP_NOERROR; - nghttp3_conn_close_stream(connection(), stream_id, app_error_code); - QuicApplication::StreamClose(stream_id, app_error_code); -} - -void Http3Application::StreamReset( - int64_t stream_id, - uint64_t app_error_code) { - nghttp3_conn_reset_stream(connection(), stream_id); - QuicApplication::StreamReset(stream_id, app_error_code); -} - -// When SendPendingData tries to send data for a given stream and there -// is no data to send but the QuicStream is still writable, it will -// be paused. When there's data available, the stream is resumed. -void Http3Application::ResumeStream(int64_t stream_id) { - nghttp3_conn_resume_stream(connection(), stream_id); -} - -// When stream data cannot be sent because of flow control, it is marked -// as being blocked. When the flow control windows expands, nghttp3 has -// to be told to unblock the stream so it knows to try sending data again. -void Http3Application::ExtendMaxStreamData( - int64_t stream_id, - uint64_t max_data) { - nghttp3_conn_unblock_stream(connection(), stream_id); -} - -// When stream data cannot be sent because of flow control, it is marked -// as being blocked. -bool Http3Application::BlockStream(int64_t stream_id) { - int err = nghttp3_conn_block_stream(connection(), stream_id); - if (err != 0) { - session()->set_last_error(QUIC_ERROR_APPLICATION, err); - return false; - } - return true; -} - -// nghttp3 keeps track of how much QuicStream data it has available and -// has sent. StreamCommit is called when a QuicPacket is serialized -// and updates nghttp3's internal state. -bool Http3Application::StreamCommit(StreamData* stream_data, size_t datalen) { - int err = nghttp3_conn_add_write_offset( - connection(), - stream_data->id, - datalen); - if (err != 0) { - session()->set_last_error(QUIC_ERROR_APPLICATION, err); - return false; - } - return true; -} - -// GetStreamData is called by SendPendingData to collect the QuicStream data -// that is to be packaged into a serialized QuicPacket. There may or may not -// be any stream data to send. The call to nghttp3_conn_writev_stream will -// provide any available stream data (if any). If nghttp3 is not sure if -// there is data to send, it will subsequently call Http3Application::ReadData -// to collect available data from the QuicStream. -int Http3Application::GetStreamData(StreamData* stream_data) { - ssize_t ret = 0; - if (connection() && session()->max_data_left()) { - ret = nghttp3_conn_writev_stream( - connection(), - &stream_data->id, - &stream_data->fin, - reinterpret_cast<nghttp3_vec*>(stream_data->data), - sizeof(stream_data->data)); - if (ret < 0) - return static_cast<int>(ret); - else - stream_data->remaining = stream_data->count = static_cast<size_t>(ret); - } - if (stream_data->id > -1) { - Debug(session(), "Selected %" PRId64 " buffers for stream %" PRId64 "%s", - stream_data->count, - stream_data->id, - stream_data->fin == 1 ? " (fin)" : ""); - } - return 0; -} - -// Determines whether SendPendingData should set fin on the QuicStream -bool Http3Application::ShouldSetFin(const StreamData& stream_data) { - return stream_data.id > -1 && - !is_control_stream(stream_data.id) && - stream_data.fin == 1; -} - -// This is where nghttp3 pulls the data from the outgoing -// buffer to prepare it to be sent on the QUIC stream. -ssize_t Http3Application::ReadData( - int64_t stream_id, - nghttp3_vec* vec, - size_t veccnt, - uint32_t* pflags) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - CHECK(stream); - - ssize_t ret = NGHTTP3_ERR_WOULDBLOCK; - - auto next = [&]( - int status, - const ngtcp2_vec* data, - size_t count, - bob::Done done) { - CHECK_LE(count, veccnt); - - switch (status) { - case bob::Status::STATUS_BLOCK: - // Fall through - case bob::Status::STATUS_WAIT: - // Fall through - case bob::Status::STATUS_EOS: - return; - case bob::Status::STATUS_END: - *pflags |= NGHTTP3_DATA_FLAG_EOF; - break; - } - - ret = count; - size_t numbytes = - nghttp3_vec_len( - reinterpret_cast<const nghttp3_vec*>(data), - count); - std::move(done)(numbytes); - - Debug(session(), "Sending %" PRIu64 " bytes for stream %" PRId64, - numbytes, stream_id); - }; - - CHECK_GE(stream->Pull( - std::move(next), - // Set OPTIONS_END here because nghttp3 takes over responsibility - // for ensuring the data all gets written out. - bob::Options::OPTIONS_END | bob::Options::OPTIONS_SYNC, - reinterpret_cast<ngtcp2_vec*>(vec), - veccnt, - kMaxVectorCount), 0); - - return ret; -} - -// Outgoing data is retained in memory until it is acknowledged. -void Http3Application::AckedStreamData(int64_t stream_id, size_t datalen) { - Acknowledge(stream_id, 0, datalen); -} - -void Http3Application::StreamClosed( - int64_t stream_id, - uint64_t app_error_code) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - if (stream) - stream->ReceiveData(1, nullptr, 0, 0); -} - -BaseObjectPtr<QuicStream> Http3Application::FindOrCreateStream( - int64_t stream_id) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - if (!stream) { - if (session()->is_graceful_closing()) { - nghttp3_conn_close_stream(connection(), stream_id, NGTCP2_ERR_CLOSING); - return {}; - } - stream = session()->CreateStream(stream_id); - nghttp3_conn_set_stream_user_data(connection(), stream_id, stream.get()); - } - CHECK(stream); - return stream; -} - -void Http3Application::ReceiveData( - int64_t stream_id, - const uint8_t* data, - size_t datalen) { - FindOrCreateStream(stream_id)->ReceiveData(0, data, datalen, 0); -} - -void Http3Application::DeferredConsume( - int64_t stream_id, - size_t consumed) { - // Do nothing here for now. nghttp3 uses the on_deferred_consume - // callback to notify when stream data that had previously been - // deferred has been delivered to the application so that the - // stream data offset can be extended. However, we extend the - // data offset from within QuicStream when the data is delivered - // so we don't have to do it here. -} - -// Called when a nghttp3 detects that a new block of headers -// has been received. Http3Application::ReceiveHeader will -// be called for each name+value pair received, then -// Http3Application::EndHeaders will be called to finalize -// the header block. -void Http3Application::BeginHeaders( - int64_t stream_id, - QuicStreamHeadersKind kind) { - Debug(session(), "Starting header block for stream %" PRId64, stream_id); - FindOrCreateStream(stream_id)->BeginHeaders(kind); -} - -// As each header name+value pair is received, it is stored internally -// by the QuicStream until stream->EndHeaders() is called, during which -// the collected headers are converted to an array and passed off to -// the javascript side. -bool Http3Application::ReceiveHeader( - int64_t stream_id, - int32_t token, - nghttp3_rcbuf* name, - nghttp3_rcbuf* value, - uint8_t flags) { - // Protect against zero-length headers (zero-length if either the - // name or value are zero-length). Such headers are simply ignored. - if (!Http3Header::IsZeroLength(name, value)) { - Debug(session(), "Receiving header for stream %" PRId64, stream_id); - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - CHECK(stream); - if (token == NGHTTP3_QPACK_TOKEN__STATUS) { - nghttp3_vec vec = nghttp3_rcbuf_get_buf(value); - if (vec.base[0] == '1') - stream->set_headers_kind(QUICSTREAM_HEADERS_KIND_INFORMATIONAL); - else - stream->set_headers_kind(QUICSTREAM_HEADERS_KIND_INITIAL); - } - auto header = std::make_unique<Http3Header>( - session()->env(), - token, - name, - value, - flags); - return stream->AddHeader(std::move(header)); - } - return true; -} - -// Marks the completion of a headers block. -void Http3Application::EndHeaders(int64_t stream_id, int64_t push_id) { - Debug(session(), "Ending header block for stream %" PRId64, stream_id); - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - CHECK(stream); - stream->EndHeaders(); -} - -void Http3Application::CancelPush( - int64_t push_id, - int64_t stream_id) { - Debug(session(), "push stream canceled"); -} - -void Http3Application::PushStream( - int64_t push_id, - int64_t stream_id) { - Debug(session(), "Received push stream %" PRIu64 " (%" PRIu64 ")", - stream_id, push_id); -} - -void Http3Application::SendStopSending( - int64_t stream_id, - uint64_t app_error_code) { - ngtcp2_conn_shutdown_stream_read( - session()->connection(), - stream_id, - app_error_code); -} - -void Http3Application::EndStream(int64_t stream_id) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - CHECK(stream); - stream->ReceiveData(1, nullptr, 0, 0); -} - -const nghttp3_conn_callbacks Http3Application::callbacks_[2] = { - // NGTCP2_CRYPTO_SIDE_CLIENT - { - OnAckedStreamData, - OnStreamClose, - OnReceiveData, - OnDeferredConsume, - OnBeginHeaders, - OnReceiveHeader, - OnEndHeaders, - OnBeginTrailers, // Begin Trailers - OnReceiveHeader, // Receive Trailer - OnEndHeaders, // End Trailers - OnBeginPushPromise, - OnReceivePushPromise, - OnEndPushPromise, - OnCancelPush, - OnSendStopSending, - OnPushStream, - OnEndStream - }, - // NGTCP2_CRYPTO_SIDE_SERVER - { - OnAckedStreamData, - OnStreamClose, - OnReceiveData, - OnDeferredConsume, - OnBeginHeaders, - OnReceiveHeader, - OnEndHeaders, - OnBeginTrailers, // Begin Trailers - OnReceiveHeader, // Receive Trailer - OnEndHeaders, // End Trailers - OnBeginPushPromise, - OnReceivePushPromise, - OnEndPushPromise, - OnCancelPush, - OnSendStopSending, - OnPushStream, - OnEndStream - } -}; - -int Http3Application::OnAckedStreamData( - nghttp3_conn* conn, - int64_t stream_id, - size_t datalen, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->AckedStreamData(stream_id, datalen); - return 0; -} - -int Http3Application::OnStreamClose( - nghttp3_conn* conn, - int64_t stream_id, - uint64_t app_error_code, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->StreamClosed(stream_id, app_error_code); - return 0; -} - -int Http3Application::OnReceiveData( - nghttp3_conn* conn, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->ReceiveData(stream_id, data, datalen); - return 0; -} - -int Http3Application::OnDeferredConsume( - nghttp3_conn* conn, - int64_t stream_id, - size_t consumed, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->DeferredConsume(stream_id, consumed); - return 0; -} - -int Http3Application::OnBeginHeaders( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->BeginHeaders(stream_id); - return 0; -} - -int Http3Application::OnBeginTrailers( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->BeginHeaders(stream_id, QUICSTREAM_HEADERS_KIND_TRAILING); - return 0; -} - -int Http3Application::OnReceiveHeader( - nghttp3_conn* conn, - int64_t stream_id, - int32_t token, - nghttp3_rcbuf* name, - nghttp3_rcbuf* value, - uint8_t flags, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - // TODO(@jasnell): Need to determine the appropriate response code here - // for when the header is not going to be accepted. - return app->ReceiveHeader(stream_id, token, name, value, flags) ? - 0 : NGHTTP3_ERR_CALLBACK_FAILURE; -} - -int Http3Application::OnEndHeaders( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->EndHeaders(stream_id); - return 0; -} - -int Http3Application::OnBeginPushPromise( - nghttp3_conn* conn, - int64_t stream_id, - int64_t push_id, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->BeginHeaders(stream_id, QUICSTREAM_HEADERS_KIND_PUSH); - return 0; -} - -int Http3Application::OnReceivePushPromise( - nghttp3_conn* conn, - int64_t stream_id, - int64_t push_id, - int32_t token, - nghttp3_rcbuf* name, - nghttp3_rcbuf* value, - uint8_t flags, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - if (!app->ReceiveHeader(stream_id, token, name, value, flags)) - return NGHTTP3_ERR_CALLBACK_FAILURE; - return 0; -} - -int Http3Application::OnEndPushPromise( - nghttp3_conn* conn, - int64_t stream_id, - int64_t push_id, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->EndHeaders(stream_id, push_id); - return 0; -} - -int Http3Application::OnCancelPush( - nghttp3_conn* conn, - int64_t push_id, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->CancelPush(push_id, stream_id); - return 0; -} - -int Http3Application::OnSendStopSending( - nghttp3_conn* conn, - int64_t stream_id, - uint64_t app_error_code, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->SendStopSending(stream_id, app_error_code); - return 0; -} - -int Http3Application::OnPushStream( - nghttp3_conn* conn, - int64_t push_id, - int64_t stream_id, - void* conn_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->PushStream(push_id, stream_id); - return 0; -} - -int Http3Application::OnEndStream( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - app->EndStream(stream_id); - return 0; -} - -ssize_t Http3Application::OnReadData( - nghttp3_conn* conn, - int64_t stream_id, - nghttp3_vec* vec, - size_t veccnt, - uint32_t* pflags, - void* conn_user_data, - void* stream_user_data) { - Http3Application* app = static_cast<Http3Application*>(conn_user_data); - return app->ReadData(stream_id, vec, veccnt, pflags); -} -} // namespace quic -} // namespace node diff --git a/src/quic/node_quic_http3_application.h b/src/quic/node_quic_http3_application.h deleted file mode 100644 index 35387414625..00000000000 --- a/src/quic/node_quic_http3_application.h +++ /dev/null @@ -1,331 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_HTTP3_APPLICATION_H_ -#define SRC_QUIC_NODE_QUIC_HTTP3_APPLICATION_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "node.h" -#include "node_http_common.h" -#include "node_mem.h" -#include "node_quic_session.h" -#include "node_quic_stream-inl.h" -#include "node_quic_util.h" -#include "v8.h" -#include <ngtcp2/ngtcp2.h> -#include <nghttp3/nghttp3.h> - -namespace node { - -namespace quic { - -constexpr uint64_t DEFAULT_QPACK_MAX_TABLE_CAPACITY = 4096; -constexpr uint64_t DEFAULT_QPACK_BLOCKED_STREAMS = 100; -constexpr size_t DEFAULT_MAX_HEADER_LIST_SIZE = 65535; -constexpr size_t DEFAULT_MAX_PUSHES = 65535; - -struct Http3RcBufferPointerTraits { - typedef nghttp3_rcbuf rcbuf_t; - typedef nghttp3_vec vector_t; - - static void inc(rcbuf_t* buf) { - nghttp3_rcbuf_incref(buf); - } - static void dec(rcbuf_t* buf) { - nghttp3_rcbuf_decref(buf); - } - static vector_t get_vec(const rcbuf_t* buf) { - return nghttp3_rcbuf_get_buf(buf); - } - static bool is_static(const rcbuf_t* buf) { - return nghttp3_rcbuf_is_static(buf); - } -}; - -struct Http3HeadersTraits { - typedef nghttp3_nv nv_t; -}; - -using Http3ConnectionPointer = DeleteFnPtr<nghttp3_conn, nghttp3_conn_del>; -using Http3RcBufferPointer = NgRcBufPointer<Http3RcBufferPointerTraits>; -using Http3Headers = NgHeaders<Http3HeadersTraits>; - -struct Http3HeaderTraits { - typedef Http3RcBufferPointer rcbufferpointer_t; - typedef QuicApplication allocator_t; - - static const char* ToHttpHeaderName(int32_t token); -}; - -using Http3Header = NgHeader<Http3HeaderTraits>; - -struct Http3ApplicationConfig : public nghttp3_conn_settings { - Http3ApplicationConfig() { - nghttp3_conn_settings_default(this); - qpack_max_table_capacity = DEFAULT_QPACK_MAX_TABLE_CAPACITY; - qpack_blocked_streams = DEFAULT_QPACK_BLOCKED_STREAMS; - max_field_section_size = DEFAULT_MAX_HEADER_LIST_SIZE; - max_pushes = DEFAULT_MAX_PUSHES; - } - uint64_t max_header_pairs = DEFAULT_MAX_HEADER_LIST_PAIRS; - uint64_t max_header_length = DEFAULT_MAX_HEADER_LENGTH; -}; - -class Http3Application; -using Http3MemoryManager = - mem::NgLibMemoryManager<Http3Application, nghttp3_mem>; - -// Http3Application is used whenever the h3 alpn identifier is used. -// It causes the QuicSession to apply HTTP/3 semantics to the connection, -// including handling of headers and other HTTP/3 specific processing. -class Http3Application final : - public QuicApplication, - public Http3MemoryManager { - public: - explicit Http3Application(QuicSession* session); - - bool Initialize() override; - - void StopTrackingMemory(void* ptr) override { - Http3MemoryManager::StopTrackingMemory(ptr); - } - - bool ReceiveStreamData( - uint32_t flags, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - uint64_t offset) override; - - void AcknowledgeStreamData( - int64_t stream_id, - uint64_t offset, - size_t datalen) override; - - void StreamClose(int64_t stream_id, uint64_t app_error_code) override; - - void StreamReset( - int64_t stream_id, - uint64_t app_error_code) override; - - void ResumeStream(int64_t stream_id) override; - - void ExtendMaxStreamData(int64_t stream_id, uint64_t max_data) override; - - bool SubmitInformation( - int64_t stream_id, - v8::Local<v8::Array> headers) override; - - bool SubmitHeaders( - int64_t stream_id, - v8::Local<v8::Array> headers, - uint32_t flags) override; - - bool SubmitTrailers( - int64_t stream_id, - v8::Local<v8::Array> headers) override; - - BaseObjectPtr<QuicStream> SubmitPush( - int64_t id, - v8::Local<v8::Array> headers) override; - - // Implementation for mem::NgLibMemoryManager - void CheckAllocatedSize(size_t previous_size) const; - void IncreaseAllocatedSize(size_t size); - void DecreaseAllocatedSize(size_t size); - - SET_SELF_SIZE(Http3Application) - SET_MEMORY_INFO_NAME(Http3Application) - void MemoryInfo(MemoryTracker* tracker) const override; - - private: - template <typename M = uint64_t, typename T> - void SetConfig(int idx, M T::*member); - - nghttp3_conn* connection() const { return connection_.get(); } - BaseObjectPtr<QuicStream> FindOrCreateStream(int64_t stream_id); - - bool CreateAndBindControlStream(); - bool CreateAndBindQPackStreams(); - int64_t CreateAndBindPushStream(int64_t push_id); - - int GetStreamData(StreamData* stream_data) override; - - bool BlockStream(int64_t stream_id) override; - bool StreamCommit(StreamData* stream_data, size_t datalen) override; - bool ShouldSetFin(const StreamData& data) override; - bool SubmitPushPromise( - int64_t id, - int64_t* push_id, - int64_t* stream_id, - const Http3Headers& headers); - bool SubmitInformation(int64_t id, const Http3Headers& headers); - bool SubmitTrailers(int64_t id, const Http3Headers& headers); - bool SubmitHeaders(int64_t id, const Http3Headers& headers, int32_t flags); - - ssize_t ReadData( - int64_t stream_id, - nghttp3_vec* vec, - size_t veccnt, - uint32_t* pflags); - - void AckedStreamData(int64_t stream_id, size_t datalen); - void StreamClosed(int64_t stream_id, uint64_t app_error_code); - void ReceiveData(int64_t stream_id, const uint8_t* data, size_t datalen); - void DeferredConsume(int64_t stream_id, size_t consumed); - void BeginHeaders( - int64_t stream_id, - QuicStreamHeadersKind kind = QUICSTREAM_HEADERS_KIND_NONE); - bool ReceiveHeader( - int64_t stream_id, - int32_t token, - nghttp3_rcbuf* name, - nghttp3_rcbuf* value, - uint8_t flags); - void EndHeaders(int64_t stream_id, int64_t push_id = 0); - void CancelPush(int64_t push_id, int64_t stream_id); - void SendStopSending(int64_t stream_id, uint64_t app_error_code); - void PushStream(int64_t push_id, int64_t stream_id); - void EndStream(int64_t stream_id); - - bool is_control_stream(int64_t stream_id) const { - return stream_id == control_stream_id_ || - stream_id == qpack_dec_stream_id_ || - stream_id == qpack_enc_stream_id_; - } - - nghttp3_mem alloc_info_; - Http3ConnectionPointer connection_; - int64_t control_stream_id_; - int64_t qpack_enc_stream_id_; - int64_t qpack_dec_stream_id_; - size_t current_nghttp3_memory_ = 0; - - Http3ApplicationConfig config_; - - void CreateConnection(); - - static const nghttp3_conn_callbacks callbacks_[2]; - - static int OnAckedStreamData( - nghttp3_conn* conn, - int64_t stream_id, - size_t datalen, - void* conn_user_data, - void* stream_user_data); - - static int OnStreamClose( - nghttp3_conn* conn, - int64_t stream_id, - uint64_t app_error_code, - void* conn_user_data, - void* stream_user_data); - - static int OnReceiveData( - nghttp3_conn* conn, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - void* conn_user_data, - void* stream_user_data); - - static int OnDeferredConsume( - nghttp3_conn* conn, - int64_t stream_id, - size_t consumed, - void* conn_user_data, - void* stream_user_data); - - static int OnBeginHeaders( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data); - - static int OnBeginTrailers( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data); - - static int OnReceiveHeader( - nghttp3_conn* conn, - int64_t stream_id, - int32_t token, - nghttp3_rcbuf* name, - nghttp3_rcbuf* value, - uint8_t flags, - void* conn_user_data, - void* stream_user_data); - - static int OnEndHeaders( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data); - - static int OnBeginPushPromise( - nghttp3_conn* conn, - int64_t stream_id, - int64_t push_id, - void* conn_user_data, - void* stream_user_data); - - static int OnReceivePushPromise( - nghttp3_conn* conn, - int64_t stream_id, - int64_t push_id, - int32_t token, - nghttp3_rcbuf* name, - nghttp3_rcbuf* value, - uint8_t flags, - void* conn_user_data, - void* stream_user_data); - - static int OnEndPushPromise( - nghttp3_conn* conn, - int64_t stream_id, - int64_t push_id, - void* conn_user_data, - void* stream_user_data); - - static int OnCancelPush( - nghttp3_conn* conn, - int64_t push_id, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data); - - static int OnSendStopSending( - nghttp3_conn* conn, - int64_t stream_id, - uint64_t app_error_code, - void* conn_user_data, - void* stream_user_data); - - static int OnPushStream( - nghttp3_conn* conn, - int64_t push_id, - int64_t stream_id, - void* conn_user_data); - - static int OnEndStream( - nghttp3_conn* conn, - int64_t stream_id, - void* conn_user_data, - void* stream_user_data); - - static ssize_t OnReadData( - nghttp3_conn* conn, - int64_t stream_id, - nghttp3_vec* vec, - size_t veccnt, - uint32_t* pflags, - void* conn_user_data, - void* stream_user_data); -}; - -} // namespace quic - -} // namespace node - -#endif // NODE_WANT_INTERNALS -#endif // SRC_QUIC_NODE_QUIC_HTTP3_APPLICATION_H_ diff --git a/src/quic/node_quic_session-inl.h b/src/quic/node_quic_session-inl.h deleted file mode 100644 index c506812b044..00000000000 --- a/src/quic/node_quic_session-inl.h +++ /dev/null @@ -1,447 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_SESSION_INL_H_ -#define SRC_QUIC_NODE_QUIC_SESSION_INL_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "debug_utils-inl.h" -#include "node_crypto.h" -#include "crypto/crypto_common.h" -#include "node_quic_crypto.h" -#include "node_quic_session.h" -#include "node_quic_socket-inl.h" -#include "node_quic_stream-inl.h" - -#include <openssl/ssl.h> -#include <memory> -#include <string> - -namespace node { - -namespace quic { - -void QuicSessionConfig::GenerateStatelessResetToken( - QuicSession* session, - const QuicCID& cid) { - transport_params.stateless_reset_token_present = 1; - StatelessResetToken token( - transport_params.stateless_reset_token, - session->socket()->session_reset_secret(), - cid); - Debug(session, "Generated stateless reset token %s for CID %s", token, cid); -} - -void QuicSessionConfig::GeneratePreferredAddressToken( - ConnectionIDStrategy connection_id_strategy, - QuicSession* session, - QuicCID* pscid) { - connection_id_strategy(session, pscid->cid(), kScidLen); - transport_params.preferred_address.cid = **pscid; - - StatelessResetToken( - transport_params.preferred_address.stateless_reset_token, - session->socket()->session_reset_secret(), - *pscid); -} - -void QuicSessionConfig::set_original_connection_id( - const QuicCID& ocid, - const QuicCID& scid) { - if (ocid) { - transport_params.original_dcid = *ocid; - transport_params.retry_scid = *scid; - transport_params.retry_scid_present = 1; - } else { - transport_params.original_dcid = *scid; - } -} - -void QuicSessionConfig::set_qlog(const ngtcp2_qlog_settings& qlog_) { - qlog = qlog_; -} - -QuicCryptoContext::QuicCryptoContext( - QuicSession* session, - BaseObjectPtr<crypto::SecureContext> secure_context, - ngtcp2_crypto_side side, - uint32_t options) : - session_(session), - secure_context_(secure_context), - side_(side), - options_(options) { - ssl_.reset(SSL_new(secure_context_->ctx_.get())); - CHECK(ssl_); -} - -void QuicCryptoContext::Initialize() { - InitializeTLS(session(), ssl_); -} - -// Cancels and frees any remaining outbound handshake data -// at each crypto level. -uint64_t QuicCryptoContext::Cancel() { - uint64_t len = handshake_[0].Cancel(); - len += handshake_[1].Cancel(); - len += handshake_[2].Cancel(); - return len; -} - -v8::MaybeLocal<v8::Value> QuicCryptoContext::ocsp_response() const { - Environment* env = session()->env(); - return crypto::GetSSLOCSPResponse( - env, - ssl_.get(), - v8::Undefined(env->isolate())); -} - -ngtcp2_crypto_level QuicCryptoContext::read_crypto_level() const { - return from_ossl_level(SSL_quic_read_level(ssl_.get())); -} - -ngtcp2_crypto_level QuicCryptoContext::write_crypto_level() const { - return from_ossl_level(SSL_quic_write_level(ssl_.get())); -} - -// TLS Keylogging is enabled per-QuicSession by attaching an handler to the -// "keylog" event. Each keylog line is emitted to JavaScript where it can -// be routed to whatever destination makes sense. Typically, this will be -// to a keylog file that can be consumed by tools like Wireshark to intercept -// and decrypt QUIC network traffic. -void QuicCryptoContext::Keylog(const char* line) { - if (UNLIKELY(session_->state_->keylog_enabled)) - session_->listener()->OnKeylog(line, strlen(line)); -} - -// Following a pause in the handshake for OCSP or client hello, we kickstart -// the handshake again here by triggering ngtcp2 to serialize data. -void QuicCryptoContext::ResumeHandshake() { - // We haven't received any actual new handshake data but calling - // this will trigger the handshake to continue. - Receive(read_crypto_level(), 0, nullptr, 0); - session_->SendPendingData(); -} - -// For 0RTT, this sets the TLS session data from the given buffer. -bool QuicCryptoContext::set_session(crypto::SSLSessionPointer session) { - if (side_ == NGTCP2_CRYPTO_SIDE_CLIENT && session != nullptr) { - set_early_data( - SSL_SESSION_get_max_early_data(session.get()) == 0xffffffffUL); - } - return crypto::SetTLSSession(ssl_, std::move(session)); -} - -v8::MaybeLocal<v8::Array> QuicCryptoContext::hello_ciphers() const { - return crypto::GetClientHelloCiphers(session()->env(), ssl_); -} - -v8::MaybeLocal<v8::Value> QuicCryptoContext::cipher_name() const { - return crypto::GetCipherName(session()->env(), ssl_); -} - -v8::MaybeLocal<v8::Value> QuicCryptoContext::cipher_version() const { - return crypto::GetCipherVersion(session()->env(), ssl_); -} - -const char* QuicCryptoContext::servername() const { - return crypto::GetServerName(ssl_.get()); -} - -const char* QuicCryptoContext::hello_alpn() const { - return crypto::GetClientHelloALPN(ssl_); -} - -const char* QuicCryptoContext::hello_servername() const { - return crypto::GetClientHelloServerName(ssl_); -} - -v8::MaybeLocal<v8::Object> QuicCryptoContext::ephemeral_key() const { - return crypto::GetEphemeralKey(session()->env(), ssl_); -} - -v8::MaybeLocal<v8::Value> QuicCryptoContext::peer_cert(bool abbreviated) const { - return crypto::GetPeerCert( - session()->env(), - ssl_, - abbreviated, - session()->is_server()); -} - -v8::MaybeLocal<v8::Value> QuicCryptoContext::cert() const { - return crypto::GetCert(session()->env(), ssl_); -} - -std::string QuicCryptoContext::selected_alpn() const { - const unsigned char* alpn_buf = nullptr; - unsigned int alpnlen; - SSL_get0_alpn_selected(ssl_.get(), &alpn_buf, &alpnlen); - return alpnlen ? - std::string(reinterpret_cast<const char*>(alpn_buf), alpnlen) : - std::string(); -} - -bool QuicCryptoContext::early_data() const { - return - (is_early_data() && - SSL_get_early_data_status(ssl_.get()) == SSL_EARLY_DATA_ACCEPTED) || - SSL_get_max_early_data(ssl_.get()) == 0xffffffffUL; -} - -void QuicCryptoContext::set_tls_alert(int err) { - Debug(session(), "TLS Alert [%d]: %s", err, SSL_alert_type_string_long(err)); - session_->set_last_error(QuicError(QUIC_ERROR_CRYPTO, err)); -} - -QuicApplication::QuicApplication(QuicSession* session) : session_(session) {} - -void QuicApplication::set_stream_fin(int64_t stream_id) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - CHECK(stream); - stream->set_fin_sent(); -} - -std::unique_ptr<QuicPacket> QuicApplication::CreateStreamDataPacket() { - return QuicPacket::Create( - "stream data", - session()->max_packet_length()); -} - -Environment* QuicApplication::env() const { - return session()->env(); -} - -// Extends the stream-level flow control by the given number of bytes. -void QuicSession::ExtendStreamOffset(int64_t stream_id, size_t amount) { - Debug(this, "Extending max stream %" PRId64 " offset by %" PRId64 " bytes", - stream_id, amount); - ngtcp2_conn_extend_max_stream_offset( - connection(), - stream_id, - amount); -} - -// Extends the connection-level flow control for the entire session by -// the given number of bytes. -void QuicSession::ExtendOffset(size_t amount) { - Debug(this, "Extending session offset by %" PRId64 " bytes", amount); - ngtcp2_conn_extend_max_offset(connection(), amount); -} - -// Copies the local transport params into the given struct for serialization. -void QuicSession::GetLocalTransportParams(ngtcp2_transport_params* params) { - CHECK(!is_destroyed()); - ngtcp2_conn_get_local_transport_params(connection(), params); -} - -// Gets the QUIC version negotiated for this QuicSession -uint32_t QuicSession::negotiated_version() const { - CHECK(!is_destroyed()); - return ngtcp2_conn_get_negotiated_version(connection()); -} - -bool QuicSession::is_handshake_completed() const { - DCHECK(!is_destroyed()); - return ngtcp2_conn_get_handshake_completed(connection()); -} - -void QuicSession::ShutdownStream(int64_t stream_id, uint64_t code) { - if (is_in_closing_period() || - is_in_draining_period() || - is_silent_closing()) { - return; // Nothing to do because we can't send any frames. - } - SendSessionScope send_scope(this); - ngtcp2_conn_shutdown_stream(connection(), stream_id, 0); -} - -// When a QuicSession hits the idle timeout, it is to be silently and -// immediately closed without attempting to send any additional data to -// the peer. All existing streams are abandoned and closed. -void QuicSession::OnIdleTimeout() { - if (!is_destroyed()) { - if (state_->idle_timeout == 1) { - Debug(this, "Idle timeout"); - Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); - return; - } - state_->idle_timeout = 1; - UpdateClosingTimer(); - } -} - -QuicCID QuicSession::dcid() const { - return QuicCID(ngtcp2_conn_get_dcid(connection())); -} - -void QuicSession::CheckAllocatedSize(size_t previous_size) const { - CHECK_GE(current_ngtcp2_memory_, previous_size); -} - -void QuicSession::IncreaseAllocatedSize(size_t size) { - current_ngtcp2_memory_ += size; -} - -void QuicSession::DecreaseAllocatedSize(size_t size) { - current_ngtcp2_memory_ -= size; -} - -uint64_t QuicSession::max_data_left() const { - return ngtcp2_conn_get_max_data_left(connection()); -} - -uint64_t QuicSession::max_local_streams_uni() const { - return ngtcp2_conn_get_max_local_streams_uni(connection()); -} - -void QuicSession::set_last_error(QuicError error) { - last_error_ = error; -} - -void QuicSession::set_last_error(int32_t family, uint64_t code) { - set_last_error({ family, code }); -} - -void QuicSession::set_last_error(int32_t family, int code) { - set_last_error({ family, code }); -} - -bool QuicSession::is_in_closing_period() const { - return ngtcp2_conn_is_in_closing_period(connection()); -} - -bool QuicSession::is_in_draining_period() const { - return ngtcp2_conn_is_in_draining_period(connection()); -} - -bool QuicSession::HasStream(int64_t id) const { - return streams_.find(id) != std::end(streams_); -} - -bool QuicSession::allow_early_data() const { - // TODO(@jasnell): For now, we always allow early data. - // Later there will be reasons we do not want to allow - // it, such as lack of available system resources. - return true; -} - -void QuicSession::SetSessionTicketAppData( - const SessionTicketAppData& app_data) { - application_->SetSessionTicketAppData(app_data); -} - -SessionTicketAppData::Status QuicSession::GetSessionTicketAppData( - const SessionTicketAppData& app_data, - SessionTicketAppData::Flag flag) { - return application_->GetSessionTicketAppData(app_data, flag); -} - -bool QuicSession::is_server() const { - return crypto_context_->side() == NGTCP2_CRYPTO_SIDE_SERVER; -} - -void QuicSession::StartGracefulClose() { - set_graceful_closing(); - RecordTimestamp(&QuicSessionStats::closing_at); -} - -// The connection ID Strategy is a function that generates -// connection ID values. By default these are generated randomly. -void QuicSession::set_connection_id_strategy(ConnectionIDStrategy strategy) { - CHECK_NOT_NULL(strategy); - connection_id_strategy_ = strategy; -} - -bool QuicSession::is_unable_to_send_packets() { - return NgCallbackScope::InNgCallbackScope(this) || - is_destroyed() || - is_in_draining_period() || - (is_server() && is_in_closing_period()) || - socket() == nullptr; -} - -void QuicSession::set_preferred_address_strategy( - PreferredAddressStrategy strategy) { - preferred_address_strategy_ = strategy; -} - -QuicSocket* QuicSession::socket() const { - return socket_.get(); -} - -// Indicates that the stream is blocked from transmitting any -// data. The specific handling of this is application specific. -// By default, we keep track of statistics but leave it up to -// the application to perform specific handling. -void QuicSession::StreamDataBlocked(int64_t stream_id) { - IncrementStat(&QuicSessionStats::block_count); - listener_->OnStreamBlocked(stream_id); -} - -// When a server advertises a preferred address in its initial -// transport parameters, ngtcp2 on the client side will trigger -// the OnSelectPreferredAdddress callback which will call this. -// The paddr argument contains the advertised preferred address. -// If the new address is going to be used, it needs to be copied -// over to dest, otherwise dest is left alone. There are two -// possible strategies that we currently support via user -// configuration: use the preferred address or ignore it. -void QuicSession::SelectPreferredAddress( - const PreferredAddress& preferred_address) { - CHECK(!is_server()); - preferred_address_strategy_(this, preferred_address); -} - -// This variant of SendPacket is used by QuicApplication -// instances to transmit a packet and update the network -// path used at the same time. -bool QuicSession::SendPacket( - std::unique_ptr<QuicPacket> packet, - const ngtcp2_path_storage& path) { - UpdateEndpoint(path.path); - return SendPacket(std::move(packet)); -} - -// Set the transport parameters received from the remote peer -void QuicSession::set_remote_transport_params() { - DCHECK(!is_destroyed()); - ngtcp2_conn_get_remote_transport_params(connection(), &transport_params_); - set_transport_params_set(); -} - -// Submits information headers only if the selected application -// supports headers. -bool QuicSession::SubmitInformation( - int64_t stream_id, - v8::Local<v8::Array> headers) { - return application_->SubmitInformation(stream_id, headers); -} - -// Submits initial headers only if the selected application -// supports headers. For http3, for instance, this is the -// method used to submit both request and response headers. -bool QuicSession::SubmitHeaders( - int64_t stream_id, - v8::Local<v8::Array> headers, - uint32_t flags) { - return application_->SubmitHeaders(stream_id, headers, flags); -} - -// Submits trailing headers only if the selected application -// supports headers. -bool QuicSession::SubmitTrailers( - int64_t stream_id, - v8::Local<v8::Array> headers) { - return application_->SubmitTrailers(stream_id, headers); -} - -// Submits a new push stream -BaseObjectPtr<QuicStream> QuicSession::SubmitPush( - int64_t stream_id, - v8::Local<v8::Array> headers) { - return application_->SubmitPush(stream_id, headers); -} - -} // namespace quic -} // namespace node - -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_SESSION_INL_H_ diff --git a/src/quic/node_quic_session.cc b/src/quic/node_quic_session.cc deleted file mode 100644 index fce6b658a43..00000000000 --- a/src/quic/node_quic_session.cc +++ /dev/null @@ -1,3963 +0,0 @@ -#include "node_quic_session-inl.h" // NOLINT(build/include) -#include "aliased_buffer.h" -#include "aliased_struct-inl.h" -#include "allocated_buffer-inl.h" -#include "async_wrap-inl.h" -#include "debug_utils-inl.h" -#include "env-inl.h" -#include "crypto/crypto_common.h" -#include "ngtcp2/ngtcp2.h" -#include "nghttp3/nghttp3.h" // NGHTTP3_ALPN_H3 -#include "ngtcp2/ngtcp2_crypto.h" -#include "ngtcp2/ngtcp2_crypto_openssl.h" -#include "node.h" -#include "node_buffer.h" -#include "node_crypto.h" -#include "node_errors.h" -#include "node_internals.h" -#include "node_http_common-inl.h" -#include "node_mem-inl.h" -#include "node_process.h" -#include "node_quic_buffer-inl.h" -#include "node_quic_crypto.h" -#include "node_quic_socket-inl.h" -#include "node_quic_stream-inl.h" -#include "node_quic_util-inl.h" -#include "node_quic_default_application.h" -#include "node_quic_http3_application.h" -#include "node_sockaddr-inl.h" -#include "stream_base-inl.h" -#include "v8.h" -#include "uv.h" - -#include <algorithm> -#include <memory> -#include <vector> -#include <string> -#include <utility> - -namespace node { - -using crypto::EntropySource; -using crypto::SecureContext; - -using v8::Array; -using v8::ArrayBufferView; -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::HandleScope; -using v8::Integer; -using v8::Local; -using v8::Number; -using v8::Object; -using v8::ObjectTemplate; -using v8::PropertyAttribute; -using v8::String; -using v8::Undefined; -using v8::Value; - -using TryCatchScope = node::errors::TryCatchScope; - -namespace quic { - -QuicCallbackScope::QuicCallbackScope(QuicSession* session) - : session_(session), - private_(new InternalCallbackScope( - session->env(), - session->object(), - { - session->get_async_id(), - session->get_trigger_async_id() - })), - try_catch_(session->env()->isolate()) { - try_catch_.SetVerbose(true); -} - -QuicCallbackScope::~QuicCallbackScope() { - Environment* env = session_->env(); - if (UNLIKELY(try_catch_.HasCaught())) { - session_->crypto_context()->set_in_client_hello(false); - session_->crypto_context()->set_in_ocsp_request(false); - if (!try_catch_.HasTerminated() && env->can_call_into_js()) { - session_->set_last_error({ - QUIC_ERROR_SESSION, - uint64_t{NGTCP2_INTERNAL_ERROR} - }); - session_->Close(); - CHECK(session_->is_destroyed()); - } - private_->MarkAsFailed(); - } -} - -typedef ssize_t(*ngtcp2_close_fn)( - ngtcp2_conn* conn, - ngtcp2_path* path, - uint8_t* dest, - size_t destlen, - uint64_t error_code, - ngtcp2_tstamp ts); - -namespace { -void SetConfig(QuicState* quic_state, int idx, uint64_t* val) { - AliasedFloat64Array& buffer = quic_state->quicsessionconfig_buffer; - uint64_t flags = static_cast<uint64_t>(buffer[IDX_QUIC_SESSION_CONFIG_COUNT]); - if (flags & (1ULL << idx)) - *val = static_cast<uint64_t>(buffer[idx]); -} - -// Forwards detailed(verbose) debugging information from ngtcp2. Enabled using -// the NODE_DEBUG_NATIVE=NGTCP2_DEBUG category. -void Ngtcp2DebugLog(void* user_data, const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - std::string format(fmt, strlen(fmt) + 1); - format[strlen(fmt)] = '\n'; - // Debug() does not work with the va_list here. So we use vfprintf - // directly instead. Ngtcp2DebugLog is only enabled when the debug - // category is enabled. - vfprintf(stderr, format.c_str(), ap); - va_end(ap); -} - -void CopyPreferredAddress( - uint8_t* dest, - size_t destlen, - uint16_t* port, - const sockaddr* addr) { - const sockaddr_in* src = reinterpret_cast<const sockaddr_in*>(addr); - memcpy(dest, &src->sin_addr, destlen); - *port = SocketAddress::GetPort(addr); -} - -ngtcp2_close_fn SelectCloseFn(uint32_t family) { - return family == QUIC_ERROR_APPLICATION ? - ngtcp2_conn_write_application_close : - ngtcp2_conn_write_connection_close; -} - -} // namespace - -std::string QuicSession::RemoteTransportParamsDebug::ToString() const { - ngtcp2_transport_params params; - ngtcp2_conn_get_remote_transport_params(session->connection(), ¶ms); - std::string out = "Remote Transport Params:\n"; - out += " Ack Delay Exponent: " + - std::to_string(params.ack_delay_exponent) + "\n"; - out += " Active Connection ID Limit: " + - std::to_string(params.active_connection_id_limit) + "\n"; - out += " Disable Active Migration: " + - std::string(params.disable_active_migration ? "Yes" : "No") + "\n"; - out += " Initial Max Data: " + - std::to_string(params.initial_max_data) + "\n"; - out += " Initial Max Stream Data Bidi Local: " + - std::to_string(params.initial_max_stream_data_bidi_local) + "\n"; - out += " Initial Max Stream Data Bidi Remote: " + - std::to_string(params.initial_max_stream_data_bidi_remote) + "\n"; - out += " Initial Max Stream Data Uni: " + - std::to_string(params.initial_max_stream_data_uni) + "\n"; - out += " Initial Max Streams Bidi: " + - std::to_string(params.initial_max_streams_bidi) + "\n"; - out += " Initial Max Streams Uni: " + - std::to_string(params.initial_max_streams_uni) + "\n"; - out += " Max Ack Delay: " + - std::to_string(params.max_ack_delay) + "\n"; - out += " Max Idle Timeout: " + - std::to_string(params.max_idle_timeout) + "\n"; - out += " Max Packet Size: " + - std::to_string(params.max_udp_payload_size) + "\n"; - - if (!session->is_server()) { - if (params.retry_scid_present) { - QuicCID cid(params.original_dcid); - QuicCID retry(params.retry_scid); - out += " Original Connection ID: " + cid.ToString() + "\n"; - out += " Retry SCID: " + retry.ToString() + "\n"; - } else { - out += " Original Connection ID: N/A \n"; - } - - if (params.preferred_address_present) { - out += " Preferred Address Present: Yes\n"; - // TODO(@jasnell): Serialize the IPv4 and IPv6 address options - } else { - out += " Preferred Address Present: No\n"; - } - - if (params.stateless_reset_token_present) { - StatelessResetToken token(params.stateless_reset_token); - out += " Stateless Reset Token: " + token.ToString() + "\n"; - } else { - out += " Stateless Reset Token: N/A"; - } - } - return out; -} - -void QuicSessionConfig::ResetToDefaults(QuicState* quic_state) { - ngtcp2_settings_default(this); - initial_ts = uv_hrtime(); - // Detailed(verbose) logging provided by ngtcp2 is only enabled - // when the NODE_DEBUG_NATIVE=NGTCP2_DEBUG category is used. - if (UNLIKELY(quic_state->env()->enabled_debug_list()->enabled( - DebugCategory::NGTCP2_DEBUG))) { - log_printf = Ngtcp2DebugLog; - } - transport_params.active_connection_id_limit = - DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; - transport_params.initial_max_stream_data_bidi_local = - DEFAULT_MAX_STREAM_DATA_BIDI_LOCAL; - transport_params.initial_max_stream_data_bidi_remote = - DEFAULT_MAX_STREAM_DATA_BIDI_REMOTE; - transport_params.initial_max_stream_data_uni = - DEFAULT_MAX_STREAM_DATA_UNI; - transport_params.initial_max_streams_bidi = - DEFAULT_MAX_STREAMS_BIDI; - transport_params.initial_max_streams_uni = - DEFAULT_MAX_STREAMS_UNI; - transport_params.initial_max_data = DEFAULT_MAX_DATA; - transport_params.max_idle_timeout = DEFAULT_MAX_IDLE_TIMEOUT; - transport_params.max_udp_payload_size = - NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; - transport_params.max_ack_delay = - NGTCP2_DEFAULT_MAX_ACK_DELAY; - transport_params.disable_active_migration = 0; - transport_params.preferred_address_present = 0; - transport_params.stateless_reset_token_present = 0; - cc_algo = NGTCP2_CC_ALGO_RENO; -} - -// Sets the QuicSessionConfig using an AliasedBuffer for efficiency. -void QuicSessionConfig::Set( - QuicState* quic_state, - const sockaddr* preferred_addr) { - ResetToDefaults(quic_state); - SetConfig(quic_state, IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT, - &transport_params.active_connection_id_limit); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL, - &transport_params.initial_max_stream_data_bidi_local); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE, - &transport_params.initial_max_stream_data_bidi_remote); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI, - &transport_params.initial_max_stream_data_uni); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_DATA, - &transport_params.initial_max_data); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_STREAMS_BIDI, - &transport_params.initial_max_streams_bidi); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_STREAMS_UNI, - &transport_params.initial_max_streams_uni); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT, - &transport_params.max_idle_timeout); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE, - &transport_params.max_udp_payload_size); - SetConfig(quic_state, IDX_QUIC_SESSION_MAX_ACK_DELAY, - &transport_params.max_ack_delay); - SetConfig(quic_state, - IDX_QUIC_SESSION_CC_ALGO, - reinterpret_cast<uint64_t *>(&cc_algo)); - - transport_params.max_idle_timeout = - transport_params.max_idle_timeout * 1000000000; - - // TODO(@jasnell): QUIC allows both IPv4 and IPv6 addresses to be - // specified. Here we're specifying one or the other. Need to - // determine if that's what we want or should we support both. - // - // TODO(@jasnell): Currently, this is specified as a single value - // that is used for all connections. In the future, it may be - // necessary to determine the preferred address based on the - // remote address. The trick, however, is that the preferred - // address must be selected before the QuicSession is created, - // before the handshake can be started. That is, it may need - // to be an optional callback on QuicSocket. That would incur - // a performance penalty so we'd really have to be sure of the - // utility. - if (preferred_addr != nullptr) { - transport_params.preferred_address_present = 1; - switch (preferred_addr->sa_family) { - case AF_INET: { - CopyPreferredAddress( - transport_params.preferred_address.ipv4_addr, - sizeof(transport_params.preferred_address.ipv4_addr), - &transport_params.preferred_address.ipv4_port, - preferred_addr); - break; - } - case AF_INET6: { - CopyPreferredAddress( - transport_params.preferred_address.ipv6_addr, - sizeof(transport_params.preferred_address.ipv6_addr), - &transport_params.preferred_address.ipv6_port, - preferred_addr); - break; - } - default: - UNREACHABLE(); - } - } -} - -void QuicSessionListener::OnKeylog(const char* line, size_t len) { - if (previous_listener_ != nullptr) - previous_listener_->OnKeylog(line, len); -} - -void QuicSessionListener::OnClientHello( - const char* alpn, - const char* server_name) { - if (previous_listener_ != nullptr) - previous_listener_->OnClientHello(alpn, server_name); -} - -QuicSessionListener::~QuicSessionListener() { - if (session_) - session_->RemoveListener(this); -} - -void QuicSessionListener::OnCert(const char* server_name) { - if (previous_listener_ != nullptr) - previous_listener_->OnCert(server_name); -} - -void QuicSessionListener::OnOCSP(Local<Value> ocsp) { - if (previous_listener_ != nullptr) - previous_listener_->OnOCSP(ocsp); -} - -void QuicSessionListener::OnStreamHeaders( - int64_t stream_id, - int kind, - const std::vector<std::unique_ptr<QuicHeader>>& headers, - int64_t push_id) { - if (previous_listener_ != nullptr) - previous_listener_->OnStreamHeaders(stream_id, kind, headers, push_id); -} - -void QuicSessionListener::OnStreamClose( - int64_t stream_id, - uint64_t app_error_code) { - if (previous_listener_ != nullptr) - previous_listener_->OnStreamClose(stream_id, app_error_code); -} - -void QuicSessionListener::OnStreamReset( - int64_t stream_id, - uint64_t app_error_code) { - if (previous_listener_ != nullptr) - previous_listener_->OnStreamReset(stream_id, app_error_code); -} - -void QuicSessionListener::OnSessionClose(QuicError error, int flags) { - if (previous_listener_ != nullptr) - previous_listener_->OnSessionClose(error, flags); -} - -void QuicSessionListener::OnStreamReady(BaseObjectPtr<QuicStream> stream) { - if (previous_listener_ != nullptr) - previous_listener_->OnStreamReady(stream); -} - -void QuicSessionListener::OnHandshakeCompleted() { - if (previous_listener_ != nullptr) - previous_listener_->OnHandshakeCompleted(); -} - -void QuicSessionListener::OnPathValidation( - ngtcp2_path_validation_result res, - const sockaddr* local, - const sockaddr* remote) { - if (previous_listener_ != nullptr) - previous_listener_->OnPathValidation(res, local, remote); -} - -void QuicSessionListener::OnSessionTicket(int size, SSL_SESSION* session) { - if (previous_listener_ != nullptr) { - previous_listener_->OnSessionTicket(size, session); - } -} - -void QuicSessionListener::OnStreamBlocked(int64_t stream_id) { - if (previous_listener_ != nullptr) { - previous_listener_->OnStreamBlocked(stream_id); - } -} - -void QuicSessionListener::OnUsePreferredAddress( - int family, - const PreferredAddress& preferred_address) { - if (previous_listener_ != nullptr) - previous_listener_->OnUsePreferredAddress(family, preferred_address); -} - -void QuicSessionListener::OnVersionNegotiation( - uint32_t supported_version, - const uint32_t* versions, - size_t vcnt) { - if (previous_listener_ != nullptr) - previous_listener_->OnVersionNegotiation(supported_version, versions, vcnt); -} - -void QuicSessionListener::OnQLog(QLogStream* qlog_stream) { - if (previous_listener_ != nullptr) - previous_listener_->OnQLog(qlog_stream); -} - -void JSQuicSessionListener::OnKeylog(const char* line, size_t len) { - Environment* env = session()->env(); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - Local<Value> line_buf; - if (!Buffer::Copy(env, line, 1 + len).ToLocal(&line_buf)) - return; - - char* data = Buffer::Data(line_buf); - data[len] = '\n'; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - USE(env->quic_on_session_keylog_function()->Call( - env->context(), - session()->object(), - 1, - &line_buf)); -} - -void JSQuicSessionListener::OnStreamBlocked(int64_t stream_id) { - Environment* env = session()->env(); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - USE(env->quic_on_stream_blocked_function()->Call( - env->context(), - stream->object(), - 0, nullptr)); -} - -void JSQuicSessionListener::OnClientHello( - const char* alpn, - const char* server_name) { - - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - // Why this instead of using MakeCallback? We need to catch any - // errors that happen both when preparing the arguments and - // invoking the callback so that we can properly signal a failure - // to the peer. - QuicCallbackScope cb_scope(session()); - - Local<Array> ciphers; - Local<Value> alpn_string = Undefined(env->isolate()); - Local<Value> servername = Undefined(env->isolate()); - - if (!session()->crypto_context()->hello_ciphers().ToLocal(&ciphers) || - (alpn != nullptr && - !String::NewFromUtf8( - env->isolate(), - alpn).ToLocal(&alpn_string)) || - (server_name != nullptr && - !String::NewFromUtf8( - env->isolate(), - server_name).ToLocal(&servername))) { - return; - } - - Local<Value> argv[] = { - alpn_string, - servername, - ciphers - }; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - USE(env->quic_on_session_client_hello_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnCert(const char* server_name) { - Environment* env = session()->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - Local<Value> servername = Undefined(env->isolate()); - if (UNLIKELY(server_name != nullptr && - !String::NewFromUtf8( - env->isolate(), - server_name, - v8::NewStringType::kNormal, - strlen(server_name)).ToLocal(&servername))) { - return; - } - - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_session_cert_function()->Call( - env->context(), - session()->object(), - 1, &servername)); -} - -void JSQuicSessionListener::OnStreamHeaders( - int64_t stream_id, - int kind, - const std::vector<std::unique_ptr<QuicHeader>>& headers, - int64_t push_id) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - MaybeStackBuffer<Local<Value>, 16> head(headers.size()); - size_t n = 0; - - QuicCallbackScope cb_scope(session()); - - for (const auto& header : headers) { - Local<Value> pair[2]; - - if (UNLIKELY(!header->GetName(session()->application()).ToLocal(&pair[0]))) - return; - - if (UNLIKELY(!header->GetValue(session()->application()).ToLocal(&pair[1]))) - return; - - head[n++] = Array::New(env->isolate(), pair, arraysize(pair)); - } - - Local<Value> argv[] = { - Number::New(env->isolate(), static_cast<double>(stream_id)), - Array::New(env->isolate(), head.out(), n), - Integer::New(env->isolate(), kind), - Undefined(env->isolate()) - }; - - if (kind == QUICSTREAM_HEADERS_KIND_PUSH) - argv[3] = Number::New(env->isolate(), static_cast<double>(push_id)); - - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_stream_headers_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnOCSP(Local<Value> ocsp) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - QuicCallbackScope cb_scope(session()); - BaseObjectPtr<QuicSession> ptr(session()); - USE(env->quic_on_session_status_function()->Call( - env->context(), - session()->object(), - 1, &ocsp)); -} - -void JSQuicSessionListener::OnStreamClose( - int64_t stream_id, - uint64_t app_error_code) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - Local<Value> argv[] = { - Number::New(env->isolate(), static_cast<double>(stream_id)), - Number::New(env->isolate(), static_cast<double>(app_error_code)) - }; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_stream_close_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnStreamReset( - int64_t stream_id, - uint64_t app_error_code) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - Local<Value> argv[] = { - Number::New(env->isolate(), static_cast<double>(stream_id)), - Number::New(env->isolate(), static_cast<double>(app_error_code)) - }; - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_stream_reset_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnSessionClose(QuicError error, int flags) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - Local<Value> argv[] = { - Number::New(env->isolate(), static_cast<double>(error.code)), - Integer::New(env->isolate(), error.family), - flags & SESSION_CLOSE_FLAG_SILENT - ? v8::True(env->isolate()) - : v8::False(env->isolate()), - flags & SESSION_CLOSE_FLAG_STATELESS_RESET - ? v8::True(env->isolate()) - : v8::False(env->isolate()) - }; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - USE(env->quic_on_session_close_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnStreamReady(BaseObjectPtr<QuicStream> stream) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - Local<Value> argv[] = { - stream->object(), - Number::New(env->isolate(), static_cast<double>(stream->id())), - Number::New(env->isolate(), static_cast<double>(stream->push_id())) - }; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_stream_ready_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnHandshakeCompleted() { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCryptoContext* ctx = session()->crypto_context(); - - QuicCallbackScope cb_scope(session()); - - Local<Value> servername = Undefined(env->isolate()); - Local<Value> validationErrorReason = v8::Null(env->isolate()); - Local<Value> validationErrorCode = v8::Null(env->isolate()); - Local<Value> cipher_name; - Local<Value> cipher_version; - - const char* hostname = ctx->servername(); - if (hostname != nullptr && - !String::NewFromUtf8(env->isolate(), hostname).ToLocal(&servername)) { - return; - } - - if (!ctx->cipher_name().ToLocal(&cipher_name) || - !ctx->cipher_version().ToLocal(&cipher_version)) { - return; - } - - int err = ctx->VerifyPeerIdentity(); - if (err != X509_V_OK && - (!crypto::GetValidationErrorReason(env, err) - .ToLocal(&validationErrorReason) || - !crypto::GetValidationErrorCode(env, err) - .ToLocal(&validationErrorCode))) { - return; - } - - Local<Value> argv[] = { - servername, - GetALPNProtocol(*session()), - cipher_name, - cipher_version, - Integer::New(env->isolate(), session()->max_pktlen_), - validationErrorReason, - validationErrorCode, - session()->crypto_context()->early_data() ? - v8::True(env->isolate()) : - v8::False(env->isolate()) - }; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_session_handshake_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnPathValidation( - ngtcp2_path_validation_result res, - const sockaddr* local, - const sockaddr* remote) { - // This is a fairly expensive operation because both the local and - // remote addresses have to converted into JavaScript objects. We - // only do this if a pathValidation handler is registered. - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Local<Context> context = env->context(); - Context::Scope context_scope(context); - - QuicCallbackScope cb_scope(session()); - - Local<Value> argv[] = { - Integer::New(env->isolate(), res), - AddressToJS(env, local), - AddressToJS(env, remote) - }; - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_session_path_validation_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnSessionTicket(int size, SSL_SESSION* sess) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - QuicCallbackScope cb_scope(session()); - - Local<Value> argv[] = { - v8::Undefined(env->isolate()), - v8::Undefined(env->isolate()) - }; - - if (size > 0) { - AllocatedBuffer session_ticket = - AllocatedBuffer::AllocateManaged(env, size); - unsigned char* session_data = - reinterpret_cast<unsigned char*>(session_ticket.data()); - memset(session_data, 0, size); - if (i2d_SSL_SESSION(sess, &session_data) > 0 && - !session_ticket.ToBuffer().ToLocal(&argv[0])) { - return; - } - } - - if (session()->is_transport_params_set() && - !Buffer::Copy(env, - reinterpret_cast<const char*>(&session()->transport_params_), - sizeof(session()->transport_params_)).ToLocal(&argv[1])) { - return; - } - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_session_ticket_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnUsePreferredAddress( - int family, - const PreferredAddress& preferred_address) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Local<Context> context = env->context(); - Context::Scope context_scope(context); - - QuicCallbackScope cb_scope(session()); - - std::string hostname = family == AF_INET ? - preferred_address.ipv4_address(): - preferred_address.ipv6_address(); - uint16_t port = - family == AF_INET ? - preferred_address.ipv4_port() : - preferred_address.ipv6_port(); - - Local<Value> argv[] = { - OneByteString(env->isolate(), hostname.c_str()), - Integer::NewFromUnsigned(env->isolate(), port), - Integer::New(env->isolate(), family) - }; - - BaseObjectPtr<QuicSession> ptr(session()); - - USE(env->quic_on_session_use_preferred_address_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnVersionNegotiation( - uint32_t supported_version, - const uint32_t* vers, - size_t vcnt) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Local<Context> context = env->context(); - Context::Scope context_scope(context); - - QuicCallbackScope cb_scope(session()); - - MaybeStackBuffer<Local<Value>, 4> versions(vcnt); - for (size_t n = 0; n < vcnt; n++) - versions[n] = Integer::New(env->isolate(), vers[n]); - - // Currently, we only support one version of QUIC but in - // the future that may change. The callback below passes - // an array back to the JavaScript side to future-proof. - Local<Value> supported = - Integer::New(env->isolate(), supported_version); - - Local<Value> argv[] = { - Integer::New(env->isolate(), NGTCP2_PROTO_VER), - Array::New(env->isolate(), versions.out(), vcnt), - Array::New(env->isolate(), &supported, 1) - }; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr<QuicSession> ptr(session()); - USE(env->quic_on_session_version_negotiation_function()->Call( - env->context(), - session()->object(), - arraysize(argv), - argv)); -} - -void JSQuicSessionListener::OnQLog(QLogStream* qlog_stream) { - CHECK_NOT_NULL(qlog_stream); - Environment* env = session()->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - QuicCallbackScope cb_scope(session()); - Local<Value> obj = qlog_stream->object(); - USE(env->quic_on_session_qlog_function()->Call( - env->context(), - session()->object(), - 1, &obj)); -} - -// Generates a new random connection ID. -void QuicSession::RandomConnectionIDStrategy( - QuicSession* session, - ngtcp2_cid* cid, - size_t cidlen) { - // CID min and max length is determined by the QUIC specification. - CHECK_LE(cidlen, NGTCP2_MAX_CIDLEN); - CHECK_GE(cidlen, NGTCP2_MIN_CIDLEN); - cid->datalen = cidlen; - // cidlen shouldn't ever be zero here but just in case that - // behavior changes in ngtcp2 in the future... - if (LIKELY(cidlen > 0)) - EntropySource(cid->data, cidlen); -} - -// Check required capabilities were not excluded from the OpenSSL build: -// - OPENSSL_NO_SSL_TRACE excludes SSL_trace() -// - OPENSSL_NO_STDIO excludes BIO_new_fp() -// HAVE_SSL_TRACE is available on the internal tcp_wrap binding for the tests. -#if defined(OPENSSL_NO_SSL_TRACE) || defined(OPENSSL_NO_STDIO) -# define HAVE_SSL_TRACE 0 -#else -# define HAVE_SSL_TRACE 1 -#endif - -QuicCryptoContext::~QuicCryptoContext() { - // Free any remaining crypto handshake data (if any) - Cancel(); -} - -void QuicCryptoContext::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("initial_crypto", handshake_[0]); - tracker->TrackField("handshake_crypto", handshake_[1]); - tracker->TrackField("app_crypto", handshake_[2]); - tracker->TrackField("ocsp_response", ocsp_response_); -} - -bool QuicCryptoContext::SetSecrets( - ngtcp2_crypto_level level, - const uint8_t* rx_secret, - const uint8_t* tx_secret, - size_t secretlen) { - - static constexpr int kCryptoKeylen = 64; - static constexpr int kCryptoIvlen = 64; - static constexpr char kQuicClientEarlyTrafficSecret[] = - "QUIC_CLIENT_EARLY_TRAFFIC_SECRET"; - static constexpr char kQuicClientHandshakeTrafficSecret[] = - "QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET"; - static constexpr char kQuicClientTrafficSecret0[] = - "QUIC_CLIENT_TRAFFIC_SECRET_0"; - static constexpr char kQuicServerHandshakeTrafficSecret[] = - "QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET"; - static constexpr char kQuicServerTrafficSecret[] = - "QUIC_SERVER_TRAFFIC_SECRET_0"; - - uint8_t rx_key[kCryptoKeylen]; - uint8_t rx_hp[kCryptoKeylen]; - uint8_t tx_key[kCryptoKeylen]; - uint8_t tx_hp[kCryptoKeylen]; - uint8_t rx_iv[kCryptoIvlen]; - uint8_t tx_iv[kCryptoIvlen]; - - if (NGTCP2_ERR(ngtcp2_crypto_derive_and_install_rx_key( - session()->connection(), - rx_key, - rx_iv, - rx_hp, - level, - rx_secret, - secretlen))) { - return false; - } - - if (NGTCP2_ERR(ngtcp2_crypto_derive_and_install_tx_key( - session()->connection(), - tx_key, - tx_iv, - tx_hp, - level, - tx_secret, - secretlen))) { - return false; - } - - switch (level) { - case NGTCP2_CRYPTO_LEVEL_EARLY: - crypto::LogSecret( - ssl_, - kQuicClientEarlyTrafficSecret, - rx_secret, - secretlen); - break; - case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - crypto::LogSecret( - ssl_, - kQuicClientHandshakeTrafficSecret, - rx_secret, - secretlen); - crypto::LogSecret( - ssl_, - kQuicServerHandshakeTrafficSecret, - tx_secret, - secretlen); - break; - case NGTCP2_CRYPTO_LEVEL_APP: - crypto::LogSecret( - ssl_, - kQuicClientTrafficSecret0, - rx_secret, - secretlen); - crypto::LogSecret( - ssl_, - kQuicServerTrafficSecret, - tx_secret, - secretlen); - break; - default: - UNREACHABLE(); - } - - return true; -} - -void QuicCryptoContext::AcknowledgeCryptoData( - ngtcp2_crypto_level level, - uint64_t datalen) { - // It is possible for the QuicSession to have been destroyed but not yet - // deconstructed. In such cases, we want to ignore the callback as there - // is nothing to do but wait for further cleanup to happen. - if (UNLIKELY(session_->is_destroyed())) - return; - Debug(session(), - "Acknowledging %" PRIu64 " crypto bytes for %s level", - datalen, - crypto_level_name(level)); - - // Consumes (frees) the given number of bytes in the handshake buffer. - handshake_[level].Consume(static_cast<size_t>(datalen)); - - // Update the statistics for the handshake, allowing us to track - // how long the handshake is taking to be acknowledged. A malicious - // peer could potentially force the QuicSession to hold on to - // crypto data for a long time by not sending an acknowledgement. - // The histogram will allow us to track the time periods between - // acknowlegements. - session()->RecordAck(&QuicSessionStats::handshake_acked_at); -} - -void QuicCryptoContext::EnableTrace() { -#if HAVE_SSL_TRACE - if (!bio_trace_) { - bio_trace_.reset(BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT)); - SSL_set_msg_callback( - ssl_.get(), - [](int write_p, - int version, - int content_type, - const void* buf, - size_t len, - SSL* ssl, - void* arg) -> void { - crypto::MarkPopErrorOnReturn mark_pop_error_on_return; - SSL_trace(write_p, version, content_type, buf, len, ssl, arg); - }); - SSL_set_msg_callback_arg(ssl_.get(), bio_trace_.get()); - } -#endif -} - -// If a 'clientHello' event listener is registered on the JavaScript -// QuicServerSession object, the STATE_CLIENT_HELLO_ENABLED state -// will be set and the OnClientHello will cause the 'clientHello' -// event to be emitted. -// -// The 'clientHello' callback will be given it's own callback function -// that must be called when the client has completed handling the event. -// The handshake will not continue until it is called. -// -// The intent here is to allow user code the ability to modify or -// replace the SecurityContext based on the server name, ALPN, or -// other handshake characteristics. -// -// The user can also set a 'cert' event handler that will be called -// when the peer certificate is received, allowing additional tweaks -// and verifications to be performed. -int QuicCryptoContext::OnClientHello() { - if (LIKELY(session_->state_->client_hello_enabled == 0)) - return 0; - - TLSCallbackScope callback_scope(this); - - if (is_in_client_hello()) - return -1; - set_in_client_hello(); - - QuicCryptoContext* ctx = session_->crypto_context(); - session_->listener()->OnClientHello( - ctx->hello_alpn(), - ctx->hello_servername()); - - // Returning -1 here will keep the TLS handshake paused until the - // client hello callback is invoked. Returning 0 means that the - // handshake is ready to proceed. When the OnClientHello callback - // is called above, it may be resolved synchronously or asynchronously. - // In case it is resolved synchronously, we need the check below. - return is_in_client_hello() ? -1 : 0; -} - -// The OnCert callback provides an opportunity to prompt the server to -// perform on OCSP request on behalf of the client (when the client -// requests it). If there is a listener for the 'OCSPRequest' event -// on the JavaScript side, the IDX_QUIC_SESSION_STATE_CERT_ENABLED -// session state slot will equal 1, which will cause the callback to -// be invoked. The callback will be given a reference to a JavaScript -// function that must be called in order for the TLS handshake to -// continue. -int QuicCryptoContext::OnOCSP() { - if (LIKELY(session_->state_->ocsp_enabled == 0)) { - Debug(session(), "No OCSPRequest handler registered"); - return 1; - } - - if (!session_->is_server()) - return 1; - - Debug(session(), "Client is requesting an OCSP Response"); - TLSCallbackScope callback_scope(this); - - // As in node_crypto.cc, this is not an error, but does suspend the - // handshake to continue when OnOCSP is complete. - if (is_in_ocsp_request()) - return -1; - set_in_ocsp_request(); - - session_->listener()->OnCert(session_->crypto_context()->servername()); - - // Returning -1 here means that we are still waiting for the OCSP - // request to be completed. When the OnCert handler is invoked - // above, it can be resolve synchronously or asynchonously. If - // resolved synchronously, we need the check below. - return is_in_ocsp_request() ? -1 : 1; -} - -void QuicCryptoContext::OnClientHelloDone( - BaseObjectPtr<SecureContext> context) { - Debug(session(), - "ClientHello completed. Context Provided? %s\n", - context ? "Yes" : "No"); - - // Continue the TLS handshake when this function exits - // otherwise it will stall and fail. - TLSHandshakeScope handshake_scope( - this, - [&]() { set_in_client_hello(false); }); - - // Disable the callback at this point so we don't loop continuously - session_->state_->client_hello_enabled = 0; - - if (context) { - int err = crypto::UseSNIContext(ssl_, context); - if (!err) { - unsigned long err = ERR_get_error(); // NOLINT(runtime/int) - return !err ? - THROW_ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT(session_->env()) : - crypto::ThrowCryptoError(session_->env(), err); - } - secure_context_ = context; - } -} - -// The OnCertDone function is called by the QuicSessionOnCertDone -// function when usercode is done handling the OCSPRequest event. -void QuicCryptoContext::OnOCSPDone(Local<Value> ocsp_response) { - Debug(session(), - "OCSPRequest completed. Response Provided? %s", - ocsp_response->IsArrayBufferView() ? "Yes" : "No"); - // Continue the TLS handshake when this function exits - // otherwise it will stall and fail. - TLSHandshakeScope handshake_scope( - this, - [&]() { set_in_ocsp_request(false); }); - - // Disable the callback at this point so we don't loop continuously - session_->state_->ocsp_enabled = 0; - - if (ocsp_response->IsArrayBufferView()) { - ocsp_response_.Reset( - session_->env()->isolate(), - ocsp_response.As<ArrayBufferView>()); - } -} - -// At this point in time, the TLS handshake secrets have been -// generated by openssl for this end of the connection and are -// ready to be used. Within this function, we need to install -// the secrets into the ngtcp2 connection object, store the -// remote transport parameters, and begin initialization of -// the QuicApplication that was selected. -bool QuicCryptoContext::OnSecrets( - ngtcp2_crypto_level level, - const uint8_t* rx_secret, - const uint8_t* tx_secret, - size_t secretlen) { - - Debug(session(), - "Received secrets for %s crypto level", - crypto_level_name(level)); - - if (!SetSecrets(level, rx_secret, tx_secret, secretlen)) - return false; - - if (level == NGTCP2_CRYPTO_LEVEL_APP) { - session_->set_remote_transport_params(); - if (!session()->InitApplication()) - return false; - } - - return true; -} - -// When the client has requested OSCP, this function will be called to provide -// the OSCP response. The OnCert() callback should have already been called -// by this point if any data is to be provided. If it hasn't, and ocsp_response_ -// is empty, no OCSP response will be sent. -int QuicCryptoContext::OnTLSStatus() { - Environment* env = session_->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - switch (side_) { - case NGTCP2_CRYPTO_SIDE_SERVER: { - if (ocsp_response_.IsEmpty()) { - Debug(session(), "There is no OCSP response"); - return SSL_TLSEXT_ERR_NOACK; - } - - Local<ArrayBufferView> obj = - PersistentToLocal::Default( - env->isolate(), - ocsp_response_); - size_t len = obj->ByteLength(); - - unsigned char* data = crypto::MallocOpenSSL<unsigned char>(len); - obj->CopyContents(data, len); - - Debug(session(), "There is an OCSP response of %d bytes", len); - - if (!SSL_set_tlsext_status_ocsp_resp(ssl_.get(), data, len)) - OPENSSL_free(data); - - ocsp_response_.Reset(); - - return SSL_TLSEXT_ERR_OK; - } - case NGTCP2_CRYPTO_SIDE_CLIENT: { - // Only invoke the callback if the ocsp handler is actually set - if (LIKELY(session_->state_->ocsp_enabled == 0)) - return 1; - Local<Value> res; - if (ocsp_response().ToLocal(&res)) - session_->listener()->OnOCSP(res); - return 1; - } - default: - UNREACHABLE(); - } -} - -// Called by ngtcp2 when a chunk of peer TLS handshake data is received. -// For every chunk, we move the TLS handshake further along until it -// is complete. -int QuicCryptoContext::Receive( - ngtcp2_crypto_level crypto_level, - uint64_t offset, - const uint8_t* data, - size_t datalen) { - if (UNLIKELY(session_->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - // Statistics are collected so we can monitor how long the - // handshake is taking to operate and complete. - if (session_->GetStat(&QuicSessionStats::handshake_start_at) == 0) - session_->RecordTimestamp(&QuicSessionStats::handshake_start_at); - session_->RecordTimestamp(&QuicSessionStats::handshake_continue_at); - - Debug(session(), "Receiving %d bytes of crypto data", datalen); - - // Internally, this passes the handshake data off to openssl - // for processing. The handshake may or may not complete. - int ret = ngtcp2_crypto_read_write_crypto_data( - session_->connection(), - crypto_level, - data, - datalen); - switch (ret) { - case 0: - return 0; - // In either of following cases, the handshake is being - // paused waiting for user code to take action (for instance - // OCSP requests or client hello modification) - case NGTCP2_CRYPTO_ERR_TLS_WANT_X509_LOOKUP: - Debug(session(), "TLS handshake wants X509 Lookup"); - return 0; - case NGTCP2_CRYPTO_ERR_TLS_WANT_CLIENT_HELLO_CB: - Debug(session(), "TLS handshake wants client hello callback"); - return 0; - default: - return ret; - } -} - -// Triggers key update to begin. This will fail and return false -// if either a previous key update is in progress and has not been -// confirmed or if the initial handshake has not yet been confirmed. -bool QuicCryptoContext::InitiateKeyUpdate() { - if (UNLIKELY(session_->is_destroyed())) - return false; - - // There's no user code that should be able to run while UpdateKey - // is running, but we need to gate on it just to be safe. - auto leave = OnScopeLeave([&]() { set_in_key_update(false); }); - CHECK(!is_in_key_update()); - set_in_key_update(); - Debug(session(), "Initiating Key Update"); - - session_->IncrementStat(&QuicSessionStats::keyupdate_count); - - return ngtcp2_conn_initiate_key_update( - session_->connection(), - uv_hrtime()) == 0; -} - -int QuicCryptoContext::VerifyPeerIdentity() { - return crypto::VerifyPeerCertificate(ssl_); -} - -// Write outbound TLS handshake data into the ngtcp2 connection -// to prepare it to be serialized. The outbound data must be -// stored in the handshake_ until it is acknowledged by the -// remote peer. It's important to keep in mind that there is -// a potential security risk here -- that is, a malicious peer -// can cause the local session to keep sent handshake data in -// memory by failing to acknowledge it or slowly acknowledging -// it. We currently do not track how much data is being buffered -// here but we do record statistics on how long the handshake -// data is foreced to be kept in memory. -void QuicCryptoContext::WriteHandshake( - ngtcp2_crypto_level level, - const uint8_t* data, - size_t datalen) { - Debug(session(), - "Writing %d bytes of %s handshake data.", - datalen, - crypto_level_name(level)); - std::unique_ptr<QuicBufferChunk> buffer = - std::make_unique<QuicBufferChunk>(datalen); - memcpy(buffer->out(), data, datalen); - session_->RecordTimestamp(&QuicSessionStats::handshake_send_at); - CHECK_EQ( - ngtcp2_conn_submit_crypto_data( - session_->connection(), - level, - buffer->out(), - datalen), 0); - handshake_[level].Push(std::move(buffer)); -} - -void QuicApplication::Acknowledge( - int64_t stream_id, - uint64_t offset, - size_t datalen) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - if (LIKELY(stream)) { - stream->Acknowledge(offset, datalen); - ResumeStream(stream_id); - } -} - -bool QuicApplication::SendPendingData() { - // The maximum number of packets to send per call - static constexpr size_t kMaxPackets = 16; - QuicPathStorage path; - std::unique_ptr<QuicPacket> packet; - uint8_t* pos = nullptr; - size_t packets_sent = 0; - int err; - - for (;;) { - ssize_t ndatalen; - StreamData stream_data; - err = GetStreamData(&stream_data); - if (err < 0) { - session()->set_last_error(QUIC_ERROR_APPLICATION, err); - return false; - } - - // If stream_data.id is -1, then we're not serializing any data for any - // specific stream. We still need to process QUIC session packets tho. - if (stream_data.id > -1) - Debug(session(), "Serializing packets for stream id %" PRId64, - stream_data.id); - else - Debug(session(), "Serializing session packets"); - - // If the packet was sent previously, then packet will have been reset. - if (!packet) { - packet = CreateStreamDataPacket(); - pos = packet->data(); - } - - ssize_t nwrite = WriteVStream(&path, pos, &ndatalen, stream_data); - - if (nwrite <= 0) { - switch (nwrite) { - case 0: - goto congestion_limited; - case NGTCP2_ERR_PKT_NUM_EXHAUSTED: - // There is a finite number of packets that can be sent - // per connection. Once those are exhausted, there's - // absolutely nothing we can do except immediately - // and silently tear down the QuicSession. This has - // to be silent because we can't even send a - // CONNECTION_CLOSE since even those require a - // packet number. - session()->Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); - return false; - case NGTCP2_ERR_STREAM_DATA_BLOCKED: - session()->StreamDataBlocked(stream_data.id); - if (session()->max_data_left() == 0) - goto congestion_limited; - // Fall through - case NGTCP2_ERR_STREAM_SHUT_WR: - if (UNLIKELY(!BlockStream(stream_data.id))) - return false; - continue; - case NGTCP2_ERR_STREAM_NOT_FOUND: - continue; - case NGTCP2_ERR_WRITE_MORE: - CHECK_GT(ndatalen, 0); - CHECK(StreamCommit(&stream_data, ndatalen)); - pos += ndatalen; - continue; - } - session()->set_last_error(QUIC_ERROR_SESSION, static_cast<int>(nwrite)); - return false; - } - - pos += nwrite; - - if (ndatalen >= 0) - CHECK(StreamCommit(&stream_data, ndatalen)); - - Debug(session(), "Sending %" PRIu64 " bytes in serialized packet", nwrite); - packet->set_length(nwrite); - if (!session()->SendPacket(std::move(packet), path)) - return false; - packet.reset(); - pos = nullptr; - MaybeSetFin(stream_data); - if (++packets_sent == kMaxPackets) - break; - } - return true; - -congestion_limited: - // We are either congestion limited or done. - if (pos - packet->data()) { - // Some data was serialized into the packet. We need to send it. - packet->set_length(pos - packet->data()); - Debug(session(), "Congestion limited, but %" PRIu64 " bytes pending", - packet->length()); - if (!session()->SendPacket(std::move(packet), path)) - return false; - } - return true; -} - -ssize_t QuicApplication::WriteVStream( - QuicPathStorage* path, - uint8_t* buf, - ssize_t* ndatalen, - const StreamData& stream_data) { - CHECK_LE(stream_data.count, kMaxVectorCount); - - uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_NONE; - if (stream_data.remaining > 0) - flags |= NGTCP2_WRITE_STREAM_FLAG_MORE; - if (stream_data.fin) - flags |= NGTCP2_WRITE_STREAM_FLAG_FIN; - - return ngtcp2_conn_writev_stream( - session()->connection(), - &path->path, - buf, - session()->max_packet_length(), - ndatalen, - flags, - stream_data.id, - stream_data.buf, - stream_data.count, - uv_hrtime()); -} - -void QuicApplication::MaybeSetFin(const StreamData& stream_data) { - if (ShouldSetFin(stream_data)) - set_stream_fin(stream_data.id); -} - -void QuicApplication::StreamHeaders( - int64_t stream_id, - int kind, - const std::vector<std::unique_ptr<QuicHeader>>& headers, - int64_t push_id) { - session()->listener()->OnStreamHeaders(stream_id, kind, headers, push_id); -} - -void QuicApplication::StreamClose( - int64_t stream_id, - uint64_t app_error_code) { - BaseObjectPtr<QuicStream> stream = session()->FindStream(stream_id); - if (stream) { - CHECK(!stream->is_destroyed()); // Should not be possible - stream->set_destroyed(); - stream->CancelPendingWrites(); - session()->RemoveStream(stream_id); - session()->listener()->OnStreamClose(stream_id, app_error_code); - } -} - -void QuicApplication::StreamReset( - int64_t stream_id, - uint64_t app_error_code) { - session()->listener()->OnStreamReset(stream_id, app_error_code); -} - -// Determines which QuicApplication variant the QuicSession will be using -// based on the alpn configured for the application. For now, this is -// determined through configuration when tghe QuicSession is created -// and is not negotiable. In the future, we may allow it to be negotiated. -QuicApplication* QuicSession::SelectApplication(QuicSession* session) { - std::string alpn = session->alpn(); - if (alpn == NGHTTP3_ALPN_H3) { - Debug(this, "Selecting HTTP/3 Application"); - return new Http3Application(session); - } - // In the future, we may end up supporting additional - // QUIC protocols. As they are added, extend the cases - // here to create and return them. - - Debug(this, "Selecting Default Application"); - return new DefaultApplication(session); -} - -// Server QuicSession Constructor -QuicSession::QuicSession( - QuicSocket* socket, - const QuicSessionConfig& config, - Local<Object> wrap, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - const QuicCID& dcid, - const QuicCID& scid, - const QuicCID& ocid, - uint32_t version, - const std::string& alpn, - uint32_t options, - QlogMode qlog) - : QuicSession( - NGTCP2_CRYPTO_SIDE_SERVER, - socket, - wrap, - socket->server_secure_context(), - AsyncWrap::PROVIDER_QUICSERVERSESSION, - alpn, - std::string(""), // empty hostname. not used on server side - dcid, - options, - nullptr) { - // The config is copied by assignment in the call below. - InitServer(config, local_addr, remote_addr, dcid, scid, ocid, version, qlog); -} - -// Client QuicSession Constructor -QuicSession::QuicSession( - QuicSocket* socket, - v8::Local<v8::Object> wrap, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - BaseObjectPtr<SecureContext> secure_context, - ngtcp2_transport_params* early_transport_params, - crypto::SSLSessionPointer early_session_ticket, - Local<Value> dcid, - PreferredAddressStrategy preferred_address_strategy, - const std::string& alpn, - const std::string& hostname, - uint32_t options, - QlogMode qlog) - : QuicSession( - NGTCP2_CRYPTO_SIDE_CLIENT, - socket, - wrap, - secure_context, - AsyncWrap::PROVIDER_QUICCLIENTSESSION, - alpn, - hostname, - QuicCID(), - options, - preferred_address_strategy) { - set_wrapped(); - InitClient( - local_addr, - remote_addr, - early_transport_params, - std::move(early_session_ticket), - dcid, - qlog); -} - -// QuicSession is an abstract base class that defines the code used by both -// server and client sessions. -QuicSession::QuicSession( - ngtcp2_crypto_side side, - QuicSocket* socket, - Local<Object> wrap, - BaseObjectPtr<SecureContext> secure_context, - AsyncWrap::ProviderType provider_type, - const std::string& alpn, - const std::string& hostname, - const QuicCID& dcid, - uint32_t options, - PreferredAddressStrategy preferred_address_strategy) - : AsyncWrap(socket->env(), wrap, provider_type), - StatsBase(socket->env(), wrap, - HistogramOptions::ACK | - HistogramOptions::RATE), - alloc_info_(MakeAllocator()), - socket_(socket), - alpn_(alpn), - hostname_(hostname), - idle_(socket->env(), [this]() { OnIdleTimeout(); }), - retransmit_(socket->env(), [this]() { OnRetransmitTimeout(); }), - dcid_(dcid), - state_(env()->isolate()), - quic_state_(socket->quic_state()) { - PushListener(&default_listener_); - set_connection_id_strategy(RandomConnectionIDStrategy); - set_preferred_address_strategy(preferred_address_strategy); - crypto_context_ = std::make_unique<QuicCryptoContext>( - - this, - secure_context, - side, - options); - application_.reset(SelectApplication(this)); - - wrap->DefineOwnProperty( - env()->context(), - env()->state_string(), - state_.GetArrayBuffer(), - PropertyAttribute::ReadOnly).Check(); - - idle_.Unref(); - retransmit_.Unref(); - - // TODO(@jasnell): memory accounting - // env_->isolate()->AdjustAmountOfExternalAllocatedMemory(kExternalSize); -} - -QuicSession::~QuicSession() { - CHECK(!NgCallbackScope::InNgCallbackScope(this)); - - // The next write should be the final one - if (qlog_stream_) - qlog_stream_->End(); - - QuicSessionListener* listener_ = listener(); - if (listener_ == listener()) - RemoveListener(listener_); - - // Stop and free the idle and retransmission timers if they are active. - // In a clean shutdown, using Close(), these will have already been - // stopped, but if Close() was not called and we're being destroyed - // in GC, for instance, we need to make sure they get stopped here. - idle_.Stop(); - retransmit_.Stop(); - - DebugStats(); -} - -template <typename Fn> -void QuicSessionStatsTraits::ToString(const QuicSession& ptr, Fn&& add_field) { -#define V(_n, name, label) \ - add_field(label, ptr.GetStat(&QuicSessionStats::name)); - SESSION_STATS(V) -#undef V -} - -void QuicSession::PushListener(QuicSessionListener* listener) { - CHECK_NOT_NULL(listener); - CHECK(!listener->session_); - - listener->previous_listener_ = listener_; - listener->session_.reset(this); - - listener_ = listener; -} - -void QuicSession::RemoveListener(QuicSessionListener* listener) { - CHECK_NOT_NULL(listener); - - QuicSessionListener* previous; - QuicSessionListener* current; - - for (current = listener_, previous = nullptr; - /* No loop condition because we want a crash if listener is not found */ - ; previous = current, current = current->previous_listener_) { - CHECK_NOT_NULL(current); - if (current == listener) { - if (previous != nullptr) - previous->previous_listener_ = current->previous_listener_; - else - listener_ = listener->previous_listener_; - break; - } - } - - listener->session_.reset(); - listener->previous_listener_ = nullptr; -} - -// The diagnostic_name is used in Debug output. -std::string QuicSession::diagnostic_name() const { - return std::string("QuicSession ") + - (is_server() ? "Server" : "Client") + - " (" + alpn().substr(1) + ", " + - std::to_string(static_cast<int64_t>(get_async_id())) + ")"; -} - -// Locate the QuicStream with the given id or return nullptr -BaseObjectPtr<QuicStream> QuicSession::FindStream(int64_t id) const { - auto it = streams_.find(id); - return it == std::end(streams_) ? BaseObjectPtr<QuicStream>() : it->second; -} - -// Invoked when ngtcp2 receives an acknowledgement for stream data. -void QuicSession::AckedStreamDataOffset( - int64_t stream_id, - uint64_t offset, - uint64_t datalen) { - Debug(this, - "Received acknowledgement for %" PRIu64 - " bytes of stream %" PRId64 " data", - datalen, stream_id); - - application_->AcknowledgeStreamData( - stream_id, - offset, - static_cast<size_t>(datalen)); -} - -// Attaches the session to the given QuicSocket. The complexity -// here is that any CID's associated with the session have to -// be associated with the new QuicSocket. -void QuicSession::AddToSocket(QuicSocket* socket) { - CHECK_NOT_NULL(socket); - Debug(this, "Adding QuicSession to %s", socket->diagnostic_name()); - socket->AddSession(scid_, BaseObjectPtr<QuicSession>(this)); - switch (crypto_context_->side()) { - case NGTCP2_CRYPTO_SIDE_SERVER: { - socket->AssociateCID(dcid_, scid_); - socket->AssociateCID(pscid_, scid_); - break; - } - case NGTCP2_CRYPTO_SIDE_CLIENT: { - std::vector<ngtcp2_cid> cids(ngtcp2_conn_get_num_scid(connection())); - ngtcp2_conn_get_scid(connection(), cids.data()); - for (const ngtcp2_cid& cid : cids) { - socket->AssociateCID(QuicCID(&cid), scid_); - } - break; - } - default: - UNREACHABLE(); - } - - std::vector<ngtcp2_cid_token> tokens( - ngtcp2_conn_get_num_active_dcid(connection())); - ngtcp2_conn_get_active_dcid(connection(), tokens.data()); - for (const ngtcp2_cid_token& token : tokens) { - if (token.token_present) { - socket->AssociateStatelessResetToken( - StatelessResetToken(token.token), - BaseObjectPtr<QuicSession>(this)); - } - } -} - -// Add the given QuicStream to this QuicSession's collection of streams. All -// streams added must be removed before the QuicSession instance is freed. -void QuicSession::AddStream(BaseObjectPtr<QuicStream> stream) { - DCHECK(!is_graceful_closing()); - Debug(this, "Adding stream %" PRId64 " to session", stream->id()); - streams_.emplace(stream->id(), stream); - - // Update tracking statistics for the number of streams associated with - // this session. - switch (stream->origin()) { - case QuicStreamOrigin::QUIC_STREAM_CLIENT: - if (is_server()) - IncrementStat(&QuicSessionStats::streams_in_count); - else - IncrementStat(&QuicSessionStats::streams_out_count); - break; - case QuicStreamOrigin::QUIC_STREAM_SERVER: - if (is_server()) - IncrementStat(&QuicSessionStats::streams_out_count); - else - IncrementStat(&QuicSessionStats::streams_in_count); - } - IncrementStat(&QuicSessionStats::streams_out_count); - switch (stream->direction()) { - case QuicStreamDirection::QUIC_STREAM_BIRECTIONAL: - IncrementStat(&QuicSessionStats::bidi_stream_count); - break; - case QuicStreamDirection::QUIC_STREAM_UNIDIRECTIONAL: - IncrementStat(&QuicSessionStats::uni_stream_count); - break; - } -} - -// Creates a new stream object and passes it off to the javascript side. -// This has to be called from within a handlescope/contextscope. -BaseObjectPtr<QuicStream> QuicSession::CreateStream(int64_t stream_id) { - CHECK(!is_destroyed()); - CHECK(!is_graceful_closing()); - CHECK(!is_closing()); - - BaseObjectPtr<QuicStream> stream = QuicStream::New(this, stream_id); - CHECK(stream); - listener()->OnStreamReady(stream); - return stream; -} - -// Initiate a shutdown of the QuicSession. -void QuicSession::Close(int close_flags) { - if (is_destroyed()) - return; - bool silent = close_flags & QuicSessionListener::SESSION_CLOSE_FLAG_SILENT; - bool stateless_reset = is_stateless_reset(); - - // If we're not running within a ngtcp2 callback scope, schedule - // a CONNECTION_CLOSE to be sent when Close exits. If we are - // within a ngtcp2 callback scope, sending the CONNECTION_CLOSE - // will be deferred. - ConnectionCloseScope close_scope(this, silent); - - // Once Close has been called, we cannot re-enter - if (UNLIKELY(is_closing())) - return; - - set_closing(); - set_silent_closing(silent); - - if (stateless_reset && silent) - close_flags |= QuicSessionListener::SESSION_CLOSE_FLAG_STATELESS_RESET; - - QuicError error = last_error(); - Debug(this, "Closing with code %" PRIu64 - " (family: %s, silent: %s, stateless reset: %s)", - error.code, - error.family_name(), - silent ? "Y" : "N", - stateless_reset ? "Y" : "N"); - - // Ensure that the QuicSession is not freed at least until after we - // exit this scope. - BaseObjectPtr<QuicSession> ptr(this); - - // If the QuicSession has been wrapped by a JS object, we have to - // notify the JavaScript side that the session is being closed. - // If it hasn't yet been wrapped, we can skip the call and and - // go straight to destroy. - if (is_wrapped()) - listener()->OnSessionClose(error, close_flags); - else - Destroy(); -} - -// Mark the QuicSession instance destroyed. This will either be invoked -// synchronously within the callstack of the QuicSession::Close() method -// or not. If it is invoked within QuicSession::Close(), the -// QuicSession::Close() will handle sending the CONNECTION_CLOSE -// frame. -void QuicSession::Destroy() { - if (is_destroyed()) - return; - - Debug(this, "Destroying the QuicSession"); - - // Mark the session destroyed. - set_destroyed(); - set_closing(false); - set_graceful_closing(false); - - // TODO(@jasnell): Allow overriding the close code - - // If we're not already in a ConnectionCloseScope, schedule - // sending a CONNECTION_CLOSE when destroy exits. If we are - // running within an ngtcp2 callback scope, sending the - // CONNECTION_CLOSE will be deferred. - ConnectionCloseScope close_scope(this, is_silent_closing()); - - // All existing streams should have already been destroyed - CHECK(streams_.empty()); - - // Stop and free the idle and retransmission timers if they are active. - idle_.Stop(); - retransmit_.Stop(); - - // The QuicSession instances are kept alive using - // BaseObjectPtr. The only persistent BaseObjectPtr - // is the map in the associated QuicSocket. Removing - // the QuicSession from the QuicSocket will free - // that pointer, allowing the QuicSession to be - // deconstructed once the stack unwinds and any - // remaining BaseObjectPtr<QuicSession> instances - // fall out of scope. - RemoveFromSocket(); -} - -// Generates and associates a new connection ID for this QuicSession. -// ngtcp2 will call this multiple times at the start of a new connection -// in order to build a pool of available CIDs. -void QuicSession::GetNewConnectionID( - ngtcp2_cid* cid, - uint8_t* token, - size_t cidlen) { - CHECK_NOT_NULL(connection_id_strategy_); - connection_id_strategy_(this, cid, cidlen); - QuicCID cid_(cid); - StatelessResetToken(token, socket()->session_reset_secret(), cid_); - socket()->AssociateCID(cid_, scid_); -} - -void QuicSession::HandleError() { - if (is_destroyed()) - return; - - // If the QuicSession is a server, send a CONNECTION_CLOSE. In either - // case, the closing timer will be set and the QuicSession will be - // destroyed. - if (is_server()) - SendConnectionClose(); - else - UpdateClosingTimer(); -} - -// The the retransmit libuv timer fires, it will call OnRetransmitTimeout, -// which determines whether or not we need to retransmit data to -// to packet loss or ack delay. -void QuicSession::OnRetransmitTimeout() { - if (is_destroyed()) - return; - uint64_t now = uv_hrtime(); - - if (ngtcp2_conn_loss_detection_expiry(connection()) <= now) { - Debug(this, "Retransmitting due to loss detection"); - IncrementStat(&QuicSessionStats::loss_retransmit_count); - } - - if (ngtcp2_conn_ack_delay_expiry(connection()) <= now) { - Debug(this, "Retransmitting due to ack delay"); - IncrementStat(&QuicSessionStats::ack_delay_retransmit_count); - } - - int rv = ngtcp2_conn_handle_expiry(connection(), now); - if (rv != 0) { - Debug(this, "Error handling retransmit timeout: %s", ngtcp2_strerror(rv)); - set_last_error(QUIC_ERROR_SESSION, rv); - HandleError(); - } - - SendPendingData(); -} - -bool QuicSession::OpenBidirectionalStream(int64_t* stream_id) { - DCHECK(!is_destroyed()); - DCHECK(!is_closing()); - DCHECK(!is_graceful_closing()); - return ngtcp2_conn_open_bidi_stream(connection(), stream_id, nullptr) == 0; -} - -bool QuicSession::OpenUnidirectionalStream(int64_t* stream_id) { - DCHECK(!is_destroyed()); - DCHECK(!is_closing()); - DCHECK(!is_graceful_closing()); - return ngtcp2_conn_open_uni_stream(connection(), stream_id, nullptr) == 0; -} - -// When ngtcp2 receives a successful response to a PATH_CHALLENGE, -// it will trigger the OnPathValidation callback which will, in turn -// invoke this. There's really nothing to do here but update stats and -// and optionally notify the javascript side if there is a handler registered. -// Notifying the JavaScript side is purely informational. -void QuicSession::PathValidation( - const ngtcp2_path* path, - ngtcp2_path_validation_result res) { - if (res == NGTCP2_PATH_VALIDATION_RESULT_SUCCESS) { - IncrementStat(&QuicSessionStats::path_validation_success_count); - } else { - IncrementStat(&QuicSessionStats::path_validation_failure_count); - } - - // Only emit the callback if there is a handler for the pathValidation - // event on the JavaScript QuicSession object. - if (UNLIKELY(state_->path_validated_enabled == 1)) { - listener_->OnPathValidation( - res, - reinterpret_cast<const sockaddr*>(path->local.addr), - reinterpret_cast<const sockaddr*>(path->remote.addr)); - } -} - -// Calling Ping will trigger the ngtcp2_conn to serialize any -// packets it currently has pending along with a probe frame -// that should keep the connection alive. This is a fire and -// forget and any errors that may occur will be ignored. The -// idle_timeout and retransmit timers will be updated. If Ping -// is called while processing an ngtcp2 callback, or if the -// closing or draining period has started, this is a non-op. -void QuicSession::Ping() { - if (NgCallbackScope::InNgCallbackScope(this) || - is_destroyed() || - is_closing() || - is_in_closing_period() || - is_in_draining_period()) { - return; - } - // TODO(@jasnell): We might want to revisit whether to handle - // errors right here. For now, we're ignoring them with the - // intent of capturing them elsewhere. - WritePackets("ping"); - UpdateIdleTimer(); - ScheduleRetransmit(); -} - -// When the QuicSocket receives a QUIC packet, it is forwarded on to here -// for processing. -bool QuicSession::Receive( - ssize_t nread, - const uint8_t* data, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags) { - - CHECK(!is_destroyed()); - - Debug(this, "Receiving QUIC packet"); - IncrementStat(&QuicSessionStats::bytes_received, nread); - - if (is_in_closing_period() && is_server()) { - Debug(this, "Packet received while in closing period"); - IncrementConnectionCloseAttempts(); - // For server QuicSession instances, we serialize the connection close - // packet once but may sent it multiple times. If the client keeps - // transmitting, then the connection close may have gotten lost. - // We don't want to send the connection close in response to - // every received packet, however, so we use an exponential - // backoff, increasing the ratio of packets received to connection - // close frame sent with every one we send. - if (UNLIKELY(ShouldAttemptConnectionClose() && - !SendConnectionClose())) { - Debug(this, "Failure sending another connection close"); - return false; - } - } - - { - // These are within a scope to ensure that the InternalCallbackScope - // and HandleScope are both exited before continuing on with the - // function. This allows any nextTicks and queued tasks to be processed - // before we continue. - auto update_stats = OnScopeLeave([&](){ - UpdateDataStats(); - }); - HandleScope handle_scope(env()->isolate()); - InternalCallbackScope callback_scope(this); - remote_address_ = remote_addr; - QuicPath path(local_addr, remote_address_); - if (!ReceivePacket(&path, data, nread)) { - HandleError(); - return false; - } - } - - // Only send pending data if we haven't entered draining mode. - // We enter the draining period when a CONNECTION_CLOSE has been - // received from the remote peer. - if (is_in_draining_period()) { - Debug(this, "In draining period after processing packet"); - // If processing the packet puts us into draining period, there's - // absolutely nothing left for us to do except silently close - // and destroy this QuicSession, which we do by updating the - // closing timer. - GetConnectionCloseInfo(); - UpdateClosingTimer(); - return true; - } - - if (!is_destroyed()) - UpdateIdleTimer(); - SendPendingData(); - Debug(this, "Successfully processed received packet"); - return true; -} - -// Performs intake processing on a received QUIC packet. The received -// data is passed on to ngtcp2 for parsing and processing. ngtcp2 will, -// in turn, invoke a series of callbacks to handle the received packet. -bool QuicSession::ReceivePacket( - ngtcp2_path* path, - const uint8_t* data, - ssize_t nread) { - CHECK(!is_destroyed()); - - uint64_t now = uv_hrtime(); - SetStat(&QuicSessionStats::received_at, now); - int err = ngtcp2_conn_read_pkt(connection(), path, data, nread, now); - if (err < 0) { - switch (err) { - case NGTCP2_ERR_CALLBACK_FAILURE: - case NGTCP2_ERR_DRAINING: - case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: - break; - case NGTCP2_ERR_RETRY: - // This should only ever happen on the server - CHECK(is_server()); - socket()->SendRetry(scid_, dcid_, local_address_, remote_address_); - Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); - break; - case NGTCP2_ERR_DROP_CONN: - Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); - break; - default: - set_last_error(QUIC_ERROR_SESSION, err); - return false; - } - } - - // If the QuicSession has been destroyed but it is not - // in the closing period, a CONNECTION_CLOSE has not yet - // been sent to the peer. Let's attempt to send one. This - // will have the effect of setting the idle timer to the - // closing/draining period, after which the QuicSession - // will be destroyed. - if (is_destroyed() && !is_in_closing_period()) { - Debug(this, "Session was destroyed while processing the packet"); - return SendConnectionClose(); - } - - return true; -} - -// Called by ngtcp2 when a chunk of stream data has been received. If -// the stream does not yet exist, it is created, then the data is -// forwarded on. -bool QuicSession::ReceiveStreamData( - uint32_t flags, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - uint64_t offset) { - auto leave = OnScopeLeave([&]() { - // Unconditionally extend the flow control window for the entire - // session but not for the individual Stream. - ExtendOffset(datalen); - }); - - return application_->ReceiveStreamData( - flags, - stream_id, - data, - datalen, - offset); -} - -// Removes the QuicSession from the current socket. This is -// done with when the session is being destroyed or being -// migrated to another QuicSocket. It is important to keep in mind -// that the QuicSocket uses a BaseObjectPtr for the QuicSession. -// If the session is removed and there are no other references held, -// the session object will be destroyed automatically. -void QuicSession::RemoveFromSocket() { - CHECK(socket_); - Debug(this, "Removing QuicSession from %s", socket_->diagnostic_name()); - if (is_server()) { - socket_->DisassociateCID(dcid_); - socket_->DisassociateCID(pscid_); - } - - std::vector<ngtcp2_cid> cids(ngtcp2_conn_get_num_scid(connection())); - std::vector<ngtcp2_cid_token> tokens( - ngtcp2_conn_get_num_active_dcid(connection())); - ngtcp2_conn_get_scid(connection(), cids.data()); - ngtcp2_conn_get_active_dcid(connection(), tokens.data()); - - for (const ngtcp2_cid& cid : cids) - socket_->DisassociateCID(QuicCID(&cid)); - - for (const ngtcp2_cid_token& token : tokens) { - if (token.token_present) { - socket_->DisassociateStatelessResetToken( - StatelessResetToken(token.token)); - } - } - - Debug(this, "Removed from the QuicSocket"); - BaseObjectPtr<QuicSocket> socket = std::move(socket_); - socket->RemoveSession(scid_, remote_address_); -} - -// Removes the given stream from the QuicSession. All streams must -// be removed before the QuicSession is destroyed. -void QuicSession::RemoveStream(int64_t stream_id) { - Debug(this, "Removing stream %" PRId64, stream_id); - - // ngtcp2 does not extend the max streams count automatically - // except in very specific conditions, none of which apply - // once we've gotten this far. We need to manually extend when - // a remote peer initiated stream is removed. - if (!is_in_draining_period() && - !is_in_closing_period() && - !is_silent_closing() && - !ngtcp2_conn_is_local_stream(connection_.get(), stream_id)) { - if (ngtcp2_is_bidi_stream(stream_id)) - ngtcp2_conn_extend_max_streams_bidi(connection_.get(), 1); - else - ngtcp2_conn_extend_max_streams_uni(connection_.get(), 1); - } - - // This will have the side effect of destroying the QuicStream - // instance. - streams_.erase(stream_id); -} - -// The retransmit timer allows us to trigger retransmission -// of packets in case they are considered lost. The exact amount -// of time is determined internally by ngtcp2 according to the -// guidelines established by the QUIC spec but we use a libuv -// timer to actually monitor. -void QuicSession::ScheduleRetransmit() { - uint64_t now = uv_hrtime(); - uint64_t expiry = ngtcp2_conn_get_expiry(connection()); - // now and expiry are in nanoseconds, interval is milliseconds - uint64_t interval = (expiry < now) ? 1 : (expiry - now) / 1000000UL; - // If interval ends up being 0, the repeating timer won't be - // scheduled, so set it to 1 instead. - if (interval == 0) interval = 1; - Debug(this, "Scheduling the retransmit timer for %" PRIu64, interval); - UpdateRetransmitTimer(interval); -} - -bool QuicSession::InitApplication() { - Debug(this, "Initializing application handler for ALPN %s", - alpn().c_str() + 1); - return application_->Initialize(); -} - -// Captures the error code and family information from a received -// connection close frame. -void QuicSession::GetConnectionCloseInfo() { - ngtcp2_connection_close_error_code close_code; - ngtcp2_conn_get_connection_close_error_code(connection(), &close_code); - set_last_error(QuicError(close_code)); -} - -// The HandshakeCompleted function is called by ngtcp2 once it -// determines that the TLS Handshake is done. The only thing we -// need to do at this point is let the javascript side know. -void QuicSession::HandshakeCompleted() { - RemoteTransportParamsDebug transport_params(this); - Debug(this, "Handshake is completed. %s", transport_params); - RecordTimestamp(&QuicSessionStats::handshake_completed_at); - if (is_server()) HandshakeConfirmed(); - listener()->OnHandshakeCompleted(); -} - -void QuicSession::HandshakeConfirmed() { - Debug(this, "Handshake is confirmed"); - RecordTimestamp(&QuicSessionStats::handshake_confirmed_at); - state_->handshake_confirmed = 1; -} - -// Every QUIC session has a remote address and local address. -// Those endpoints can change through the lifetime of a connection, -// so whenever a packet is successfully processed, or when a -// response is to be sent, we have to keep track of the path -// and update as we go. -void QuicSession::UpdateEndpoint(const ngtcp2_path& path) { - remote_address_.Update(path.remote.addr, path.remote.addrlen); - local_address_.Update(path.local.addr, path.local.addrlen); - - // If the updated remote address is IPv6, set the flow label - if (remote_address_.family() == AF_INET6) { - // TODO(@jasnell): Currently, this reuses the session reset secret. - // That may or may not be a good idea, we need to verify and may - // need to have a distinct secret for flow labels. - uint32_t flow_label = - GenerateFlowLabel( - local_address_, - remote_address_, - scid_, - socket()->session_reset_secret(), - NGTCP2_STATELESS_RESET_TOKENLEN); - remote_address_.set_flow_label(flow_label); - } -} - -// Called by the OnVersionNegotiation callback when a version -// negotiation frame has been received by the client. The sv -// parameter is an array of versions supported by the remote peer. -void QuicSession::VersionNegotiation(const uint32_t* sv, size_t nsv) { - CHECK(!is_server()); - listener()->OnVersionNegotiation(NGTCP2_PROTO_VER, sv, nsv); -} - -// The retransmit timer allows us to trigger retransmission -// of packets in case they are considered lost. The exact amount -// of time is determined internally by ngtcp2 according to the -// guidelines established by the QUIC spec but we use a libuv -// timer to actually monitor. Here we take the calculated timeout -// and extend out the libuv timer. -void QuicSession::UpdateRetransmitTimer(uint64_t timeout) { - retransmit_.Update(timeout, timeout); -} - -void QuicSession::IncrementConnectionCloseAttempts() { - if (connection_close_attempts_ < kMaxSizeT) - connection_close_attempts_++; -} - -bool QuicSession::ShouldAttemptConnectionClose() { - if (connection_close_attempts_ == connection_close_limit_) { - if (connection_close_limit_ * 2 <= kMaxSizeT) - connection_close_limit_ *= 2; - else - connection_close_limit_ = kMaxSizeT; - return true; - } - return false; -} - -// Transmits either a protocol or application connection -// close to the peer. The choice of which is send is -// based on the current value of last_error_. -bool QuicSession::SendConnectionClose() { - CHECK(!NgCallbackScope::InNgCallbackScope(this)); - - // Do not send any frames at all if we're in the draining period - // or in the middle of a silent close - if (is_in_draining_period() || is_silent_closing()) - return true; - - // The specific handling of connection close varies for client - // and server QuicSession instances. For servers, we will - // serialize the connection close once but may end up transmitting - // it multiple times; whereas for clients, we will serialize it - // once and send once only. - QuicError error = last_error(); - Debug(this, "Sending connection close with code: %" PRIu64 " (family: %s)", - error.code, error.family_name()); - - UpdateClosingTimer(); - - // If initial keys have not yet been installed, use the alternative - // ImmediateConnectionClose to send a stateless connection close to - // the peer. - if (crypto_context()->write_crypto_level() == - NGTCP2_CRYPTO_LEVEL_INITIAL) { - socket()->ImmediateConnectionClose( - dcid(), - scid_, - local_address_, - remote_address_, - error.code); - return true; - } - - switch (crypto_context_->side()) { - case NGTCP2_CRYPTO_SIDE_SERVER: { - if (!is_in_closing_period() && !StartClosingPeriod()) { - Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); - return false; - } - CHECK_GT(conn_closebuf_->length(), 0); - return SendPacket(QuicPacket::Copy(conn_closebuf_)); - } - case NGTCP2_CRYPTO_SIDE_CLIENT: { - auto packet = QuicPacket::Create("client connection close"); - - ssize_t nwrite = - SelectCloseFn(error.family)( - connection(), - nullptr, - packet->data(), - max_pktlen_, - error.code, - uv_hrtime()); - if (UNLIKELY(nwrite < 0)) { - Debug(this, "Error writing connection close: %d", nwrite); - set_last_error(QUIC_ERROR_SESSION, static_cast<int>(nwrite)); - Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); - return false; - } - packet->set_length(nwrite); - return SendPacket(std::move(packet)); - } - default: - UNREACHABLE(); - } -} - -void QuicSession::IgnorePreferredAddressStrategy( - QuicSession* session, - const PreferredAddress& preferred_address) { - Debug(session, "Ignoring server preferred address"); -} - -void QuicSession::UsePreferredAddressStrategy( - QuicSession* session, - const PreferredAddress& preferred_address) { - int family = session->socket()->local_address().family(); - if (preferred_address.Use(family)) { - Debug(session, "Using server preferred address"); - // Emit only if the QuicSession has a usePreferredAddress handler - // on the JavaScript side. - if (UNLIKELY(session->state_->use_preferred_address_enabled == 1)) { - session->listener()->OnUsePreferredAddress(family, preferred_address); - } - } else { - // If Use returns false, the advertised preferred address does not - // match the current local preferred endpoint IP version. - Debug(session, - "Not using server preferred address due to IP version mismatch"); - } -} - -// Passes a serialized packet to the associated QuicSocket. -bool QuicSession::SendPacket(std::unique_ptr<QuicPacket> packet) { - CHECK(!is_in_draining_period()); - - // There's nothing to send. - if (packet->length() == 0) - return true; - - IncrementStat(&QuicSessionStats::bytes_sent, packet->length()); - RecordTimestamp(&QuicSessionStats::sent_at); -// ScheduleRetransmit(); - - Debug(this, "Sending %" PRIu64 " bytes to %s from %s", - packet->length(), - remote_address_, - local_address_); - - int err = socket()->SendPacket( - local_address_, - remote_address_, - std::move(packet), - BaseObjectPtr<QuicSession>(this)); - - if (err != 0) { - set_last_error(QUIC_ERROR_SESSION, err); - return false; - } - - return true; -} - -// Sends any pending handshake or session packet data. -void QuicSession::SendPendingData() { - if (is_unable_to_send_packets()) - return; - - Debug(this, "Sending pending data"); - if (!application_->SendPendingData()) { - Debug(this, "Error sending QUIC application data"); - HandleError(); - } - ScheduleRetransmit(); -} - -// When completing the TLS handshake, the TLS session information -// is provided to the QuicSession so that the session ticket and -// the remote transport parameters can be captured to support 0RTT -// session resumption. -int QuicSession::set_session(SSL_SESSION* session) { - CHECK(!is_server()); - CHECK(!is_destroyed()); - int size = i2d_SSL_SESSION(session, nullptr); - if (size > SecureContext::kMaxSessionSize) - return 0; - listener_->OnSessionTicket(size, session); - return 0; -} - -// A client QuicSession can be migrated to a different QuicSocket instance. -bool QuicSession::set_socket(QuicSocket* socket, bool nat_rebinding) { - CHECK(!is_server()); - CHECK(!is_destroyed()); - - if (is_graceful_closing()) - return false; - - if (socket == nullptr || socket == socket_.get()) - return true; - - Debug(this, "Migrating to %s", socket->diagnostic_name()); - - // Ensure that we maintain a reference to keep this from being - // destroyed while we are starting the migration. - BaseObjectPtr<QuicSession> ptr(this); - - // Step 1: Remove the session from the current socket - RemoveFromSocket(); - - // Step 2: Add this Session to the given Socket - AddToSocket(socket); - - // Step 3: Update the internal references and make sure - // we are listening. - socket_.reset(socket); - socket->ReceiveStart(); - - // Step 4: Update ngtcp2 - auto local_address = socket->local_address(); - - // The nat_rebinding option here should rarely, if ever - // be used in a real application. It is intended to serve - // as a way of simulating a silent local address change, - // such as when the NAT binding changes. Currently, Node.js - // does not really have an effective way of detecting that. - // Manual user code intervention to handle the migration - // to the new QuicSocket is required, which should always - // trigger path validation using the ngtcp2_conn_initiate_migration. - if (LIKELY(!nat_rebinding)) { - SendSessionScope send(this); - QuicPath path(local_address, remote_address_); - return ngtcp2_conn_initiate_migration( - connection(), - &path, - uv_hrtime()) == 0; - } else { - ngtcp2_addr addr; - ngtcp2_conn_set_local_addr( - connection(), - ngtcp2_addr_init( - &addr, - local_address.data(), - local_address.length(), - nullptr)); - } - - return true; -} - -void QuicSession::ResumeStream(int64_t stream_id) { - application()->ResumeStream(stream_id); -} - -// Begin connection close by serializing the CONNECTION_CLOSE packet. -// There are two variants: one to serialize an application close, the -// other to serialize a protocol close. The frames are generally -// identical with the exception of a bit in the header. On server -// QuicSessions, we serialize the frame once and may retransmit it -// multiple times. On client QuicSession instances, we only ever -// serialize the connection close once. -bool QuicSession::StartClosingPeriod() { - if (is_destroyed()) - return false; - if (is_in_closing_period()) - return true; - - QuicError error = last_error(); - Debug(this, "Closing period has started. Error %d", error.code); - - // Once the CONNECTION_CLOSE packet is written, - // is_in_closing_period will return true. - conn_closebuf_ = QuicPacket::Create("server connection close"); - ssize_t nwrite = - SelectCloseFn(error.family)( - connection(), - nullptr, - conn_closebuf_->data(), - max_pktlen_, - error.code, - uv_hrtime()); - if (nwrite < 0) { - set_last_error(QUIC_ERROR_SESSION, static_cast<int>(nwrite)); - return false; - } - conn_closebuf_->set_length(nwrite); - return true; -} - -// Called by ngtcp2 when a stream has been closed. If the stream does -// not exist, the close is ignored. -void QuicSession::StreamClose(int64_t stream_id, uint64_t app_error_code) { - Debug(this, "Closing stream %" PRId64 " with code %" PRIu64, - stream_id, - app_error_code); - - application_->StreamClose(stream_id, app_error_code); -} - -// Called when the QuicSession has received a RESET_STREAM frame from the -// peer, indicating that it will no longer send additional frames for the -// stream. If the stream is not yet known, reset is ignored. If the stream -// has already received a STREAM frame with fin set, the stream reset is -// ignored (the QUIC spec permits implementations to handle this situation -// however they want.) If the stream has not yet received a STREAM frame -// with the fin set, then the RESET_STREAM causes the readable side of the -// stream to be abruptly closed and any additional stream frames that may -// be received will be discarded if their offset is greater than final_size. -// On the JavaScript side, receiving a C is undistinguishable from -// a normal end-of-stream. No additional data events will be emitted, the -// end event will be emitted, and the readable side of the duplex will be -// closed. -// -// If the stream is still writable, no additional action is taken. If, -// however, the writable side of the stream has been closed (or was never -// open in the first place as in the case of peer-initiated unidirectional -// streams), the reset will cause the stream to be immediately destroyed. -void QuicSession::StreamReset( - int64_t stream_id, - uint64_t final_size, - uint64_t app_error_code) { - Debug(this, - "Reset stream %" PRId64 " with code %" PRIu64 - " and final size %" PRIu64, - stream_id, - app_error_code, - final_size); - - BaseObjectPtr<QuicStream> stream = FindStream(stream_id); - - if (stream) { - stream->set_final_size(final_size); - application_->StreamReset(stream_id, app_error_code); - } -} - -void QuicSession::UpdateConnectionID( - int type, - const QuicCID& cid, - const StatelessResetToken& token) { - switch (type) { - case NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE: - socket_->AssociateStatelessResetToken( - token, - BaseObjectPtr<QuicSession>(this)); - break; - case NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE: - socket_->DisassociateStatelessResetToken(token); - break; - } -} - -// Updates the idle timer timeout. If the idle timer fires, the connection -// will be silently closed. It is important to update this as activity -// occurs to keep the idle timer from firing. -void QuicSession::UpdateIdleTimer() { - if (is_closing_timer_enabled()) - return; - uint64_t now = uv_hrtime(); - uint64_t expiry = ngtcp2_conn_get_idle_expiry(connection()); - // nano to millis - uint64_t timeout = expiry > now ? (expiry - now) / 1000000ULL : 1; - if (timeout == 0) timeout = 1; - Debug(this, "Updating idle timeout to %" PRIu64, timeout); - idle_.Update(timeout, timeout); -} - -void QuicSession::UpdateClosingTimer() { - set_closing_timer_enabled(true); - uint64_t timeout = - is_server() ? (ngtcp2_conn_get_pto(connection()) / 1000000ULL) * 3 : 0; - Debug(this, "Setting closing timeout to %" PRIu64, timeout); - retransmit_.Stop(); - idle_.Update(timeout, 0); - idle_.Ref(); -} - -// Write any packets current pending for the ngtcp2 connection based on -// the current state of the QuicSession. If the QuicSession is in the -// closing period, only CONNECTION_CLOSE packets may be written. If the -// QuicSession is in the draining period, no packets may be written. -// -// Packets are flushed to the underlying QuicSocket uv_udp_t as soon -// as they are written. The WritePackets method may cause zero or more -// packets to be serialized. -// -// If there are any acks or retransmissions pending, those will be -// serialized at this point as well. However, WritePackets does not -// serialize stream data that is being sent initially. -bool QuicSession::WritePackets(const char* diagnostic_label) { - CHECK(!NgCallbackScope::InNgCallbackScope(this)); - - // During either the draining or closing period, - // we are not permitted to send any additional packets. - if (is_in_draining_period() || is_in_closing_period()) - return true; - - // Otherwise, serialize and send pending frames - QuicPathStorage path; - for (;;) { - auto packet = QuicPacket::Create(diagnostic_label, max_pktlen_); - // ngtcp2_conn_write_pkt will fill the created QuicPacket up - // as much as possible, and then should be called repeatedly - // until it returns 0 or fatally errors. On each call, it - // will return the number of bytes encoded into the packet. - ssize_t nwrite = - ngtcp2_conn_write_pkt( - connection(), - &path.path, - packet->data(), - max_pktlen_, - uv_hrtime()); - - if (nwrite <= 0) { - switch (nwrite) { - case 0: - // There was nothing to write. - return true; - case NGTCP2_ERR_PKT_NUM_EXHAUSTED: - // There is a finite number of packets that can be sent - // per connection. Once those are exhausted, there's - // absolutely nothing we can do except immediately - // and silently tear down the QuicSession. This has - // to be silent because we can't even send a - // CONNECTION_CLOSE since even those require a - // packet number. - Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); - return false; - default: - set_last_error(QUIC_ERROR_SESSION, static_cast<int>(nwrite)); - return false; - } - } - - packet->set_length(nwrite); - UpdateEndpoint(path.path); - UpdateDataStats(); - - if (!SendPacket(std::move(packet))) - return false; - } -} - -void QuicSession::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("crypto_context", crypto_context_.get()); - tracker->TrackField("alpn", alpn_); - tracker->TrackField("hostname", hostname_); - tracker->TrackField("idle", idle_); - tracker->TrackField("retransmit", retransmit_); - tracker->TrackField("streams", streams_); - tracker->TrackFieldWithSize("current_ngtcp2_memory", current_ngtcp2_memory_); - tracker->TrackField("conn_closebuf", conn_closebuf_); - tracker->TrackField("application", application_); - tracker->TrackField("quic_state", quic_state_); - tracker->TrackField("qlog_stream", qlog_stream_); - StatsBase::StatsMemoryInfo(tracker); -} - -void QuicSession::ExtendMaxStreamsBidi(uint64_t max_streams) { - state_->max_streams_bidi = max_streams; -} - -void QuicSession::ExtendMaxStreamsUni(uint64_t max_streams) { - state_->max_streams_uni = max_streams; -} - -void QuicSession::ExtendMaxStreamsRemoteUni(uint64_t max_streams) { - Debug(this, "Extend remote max unidirectional streams: %" PRIu64, - max_streams); - application_->ExtendMaxStreamsRemoteUni(max_streams); -} - -void QuicSession::ExtendMaxStreamsRemoteBidi(uint64_t max_streams) { - Debug(this, "Extend remote max bidirectional streams: %" PRIu64, - max_streams); - application_->ExtendMaxStreamsRemoteBidi(max_streams); -} - -void QuicSession::ExtendMaxStreamData(int64_t stream_id, uint64_t max_data) { - Debug(this, - "Extending max stream %" PRId64 " data to %" PRIu64, - stream_id, max_data); - application_->ExtendMaxStreamData(stream_id, max_data); -} - -// Static function to create a new server QuicSession instance -BaseObjectPtr<QuicSession> QuicSession::CreateServer( - QuicSocket* socket, - const QuicSessionConfig& config, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - const QuicCID& dcid, - const QuicCID& scid, - const QuicCID& ocid, - uint32_t version, - const std::string& alpn, - uint32_t options, - QlogMode qlog) { - Local<Object> obj; - if (!socket->env() - ->quicserversession_instance_template() - ->NewInstance(socket->env()->context()).ToLocal(&obj)) { - return {}; - } - BaseObjectPtr<QuicSession> session = - MakeDetachedBaseObject<QuicSession>( - socket, - config, - obj, - local_addr, - remote_addr, - dcid, - scid, - ocid, - version, - alpn, - options, - qlog); - - session->AddToSocket(socket); - return session; -} - -// Initializes a newly created server QuicSession. -void QuicSession::InitServer( - QuicSessionConfig config, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - const QuicCID& dcid, - const QuicCID& scid, - const QuicCID& ocid, - uint32_t version, - QlogMode qlog) { - - CHECK_NULL(connection_); - - ExtendMaxStreamsBidi(DEFAULT_MAX_STREAMS_BIDI); - ExtendMaxStreamsUni(DEFAULT_MAX_STREAMS_UNI); - - local_address_ = local_addr; - remote_address_ = remote_addr; - max_pktlen_ = GetMaxPktLen(remote_addr); - - config.set_original_connection_id(ocid, scid); - - connection_id_strategy_(this, scid_.cid(), kScidLen); - - config.GenerateStatelessResetToken(this, scid_); - config.GeneratePreferredAddressToken(connection_id_strategy_, this, &pscid_); - - QuicPath path(local_addr, remote_address_); - - // NOLINTNEXTLINE(readability/pointer_notation) - if (qlog == QlogMode::kEnabled) config.set_qlog({ *ocid, OnQlogWrite }); - - ngtcp2_conn* conn; - CHECK_EQ( - ngtcp2_conn_server_new( - &conn, - dcid.cid(), - scid_.cid(), - &path, - version, - &callbacks[crypto_context_->side()], - &config, - &alloc_info_, - static_cast<QuicSession*>(this)), 0); - - connection_.reset(conn); - - crypto_context_->Initialize(); - UpdateDataStats(); - UpdateIdleTimer(); -} - -namespace { -void QuicSessionOnClientHelloDone(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - Local<FunctionTemplate> cons = env->secure_context_constructor_template(); - SecureContext* context = nullptr; - if (args[0]->IsObject() && cons->HasInstance(args[0])) - context = Unwrap<SecureContext>(args[0].As<Object>()); - session->crypto_context()->OnClientHelloDone( - BaseObjectPtr<SecureContext>(context)); -} - -void QuicSessionOnCertDone(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - session->crypto_context()->OnOCSPDone(args[0]); -} -} // namespace - - -// Data stats are used to allow user code to keep track of important -// statistics such as amount of data in flight through the lifetime -// of a connection. -void QuicSession::UpdateDataStats() { - if (is_destroyed()) - return; - state_->max_data_left = ngtcp2_conn_get_max_data_left(connection()); - - ngtcp2_conn_stat stat; - ngtcp2_conn_get_conn_stat(connection(), &stat); - - SetStat(&QuicSessionStats::latest_rtt, stat.latest_rtt); - SetStat(&QuicSessionStats::min_rtt, stat.min_rtt); - SetStat(&QuicSessionStats::smoothed_rtt, stat.smoothed_rtt); - SetStat(&QuicSessionStats::receive_rate, stat.recv_rate_sec); - SetStat(&QuicSessionStats::send_rate, stat.delivery_rate_sec); - SetStat(&QuicSessionStats::cwnd, stat.cwnd); - - state_->bytes_in_flight = stat.bytes_in_flight; - // The max_bytes_in_flight is a highwater mark that can be used - // in performance analysis operations. - if (stat.bytes_in_flight > GetStat(&QuicSessionStats::max_bytes_in_flight)) - SetStat(&QuicSessionStats::max_bytes_in_flight, stat.bytes_in_flight); -} - -// Static method for creating a new client QuicSession instance. -BaseObjectPtr<QuicSession> QuicSession::CreateClient( - QuicSocket* socket, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - BaseObjectPtr<SecureContext> secure_context, - ngtcp2_transport_params* early_transport_params, - crypto::SSLSessionPointer early_session_ticket, - Local<Value> dcid, - PreferredAddressStrategy preferred_address_strategy, - const std::string& alpn, - const std::string& hostname, - uint32_t options, - QlogMode qlog) { - Local<Object> obj; - if (!socket->env() - ->quicclientsession_instance_template() - ->NewInstance(socket->env()->context()).ToLocal(&obj)) { - return {}; - } - - BaseObjectPtr<QuicSession> session = - MakeDetachedBaseObject<QuicSession>( - socket, - obj, - local_addr, - remote_addr, - secure_context, - early_transport_params, - std::move(early_session_ticket), - dcid, - preferred_address_strategy, - alpn, - hostname, - options, - qlog); - - session->AddToSocket(socket); - - return session; -} - -// Initialize a newly created client QuicSession. -// The early_transport_params and session_ticket are optional to -// perform a 0RTT resumption of a prior session. -// The dcid_value parameter is optional to allow user code the -// ability to provide an explicit dcid (this should be rare) -void QuicSession::InitClient( - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - ngtcp2_transport_params* early_transport_params, - crypto::SSLSessionPointer early_session_ticket, - Local<Value> dcid_value, - QlogMode qlog) { - CHECK_NULL(connection_); - - local_address_ = local_addr; - remote_address_ = remote_addr; - Debug(this, "Initializing connection from %s to %s", - local_address_, - remote_address_); - - // The maximum packet length is determined largely - // by the IP version (IPv4 vs IPv6). Packet sizes - // should be limited to the maximum MTU necessary to - // prevent IP fragmentation. - max_pktlen_ = GetMaxPktLen(remote_address_); - - QuicSessionConfig config(quic_state()); - ExtendMaxStreamsBidi(DEFAULT_MAX_STREAMS_BIDI); - ExtendMaxStreamsUni(DEFAULT_MAX_STREAMS_UNI); - - connection_id_strategy_(this, scid_.cid(), NGTCP2_MAX_CIDLEN); - - ngtcp2_cid dcid; - if (dcid_value->IsArrayBufferView()) { - ArrayBufferViewContents<uint8_t> sbuf( - dcid_value.As<ArrayBufferView>()); - CHECK_LE(sbuf.length(), NGTCP2_MAX_CIDLEN); - CHECK_GE(sbuf.length(), NGTCP2_MIN_CIDLEN); - memcpy(dcid.data, sbuf.data(), sbuf.length()); - dcid.datalen = sbuf.length(); - } else { - connection_id_strategy_(this, &dcid, NGTCP2_MAX_CIDLEN); - } - - QuicPath path(local_address_, remote_address_); - - if (qlog == QlogMode::kEnabled) config.set_qlog({ dcid, OnQlogWrite }); - - ngtcp2_conn* conn; - CHECK_EQ( - ngtcp2_conn_client_new( - &conn, - &dcid, - scid_.cid(), - &path, - NGTCP2_PROTO_VER, - &callbacks[crypto_context_->side()], - &config, - &alloc_info_, - static_cast<QuicSession*>(this)), 0); - - - connection_.reset(conn); - - crypto_context_->Initialize(); - - if (early_transport_params != nullptr) - ngtcp2_conn_set_early_remote_transport_params(conn, early_transport_params); - crypto_context_->set_session(std::move(early_session_ticket)); - - UpdateIdleTimer(); - UpdateDataStats(); -} - -// Static ngtcp2 callbacks are registered when ngtcp2 when a new ngtcp2_conn is -// created. These are static functions that, for the most part, simply defer to -// a QuicSession instance that is passed through as user_data. - -// Called by ngtcp2 for both client and server connections when -// TLS handshake data has been received and needs to be processed. -// This will be called multiple times during the TLS handshake -// process and may be called during key updates. -int QuicSession::OnReceiveCryptoData( - ngtcp2_conn* conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, - const uint8_t* data, - size_t datalen, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - int ret = session->crypto_context()->Receive( - crypto_level, - offset, - data, - datalen); - return ret == 0 ? 0 : NGTCP2_ERR_CALLBACK_FAILURE; -} - -// Called by ngtcp2 for both client and server connections -// when a request to extend the maximum number of bidirectional -// streams has been received. -int QuicSession::OnExtendMaxStreamsBidi( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->ExtendMaxStreamsBidi(max_streams); - return 0; -} - -// Called by ngtcp2 for both client and server connections -// when a request to extend the maximum number of unidirectional -// streams has been received -int QuicSession::OnExtendMaxStreamsUni( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->ExtendMaxStreamsUni(max_streams); - return 0; -} - -// Triggered by ngtcp2 when the local peer has received an -// indication from the remote peer indicating that additional -// unidirectional streams may be sent. The max_streams parameter -// identifies the highest unidirectional stream ID that may be -// opened. -int QuicSession::OnExtendMaxStreamsRemoteUni( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->ExtendMaxStreamsRemoteUni(max_streams); - return 0; -} - -// Triggered by ngtcp2 when the local peer has received an -// indication from the remote peer indicating that additional -// bidirectional streams may be sent. The max_streams parameter -// identifies the highest bidirectional stream ID that may be -// opened. -int QuicSession::OnExtendMaxStreamsRemoteBidi( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->ExtendMaxStreamsRemoteUni(max_streams); - return 0; -} - -// Triggered by ngtcp2 when the local peer has received a flow -// control signal from the remote peer indicating that additional -// data can be sent. The max_data parameter identifies the maximum -// data offset that may be sent. That is, a value of 99 means that -// out of a stream of 1000 bytes, only the first 100 may be sent. -// (offsets 0 through 99). -int QuicSession::OnExtendMaxStreamData( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t max_data, - void* user_data, - void* stream_user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->ExtendMaxStreamData(stream_id, max_data); - return 0; -} - -int QuicSession::OnConnectionIDStatus( - ngtcp2_conn* conn, - int type, - uint64_t seq, - const ngtcp2_cid* cid, - const uint8_t* token, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - if (token != nullptr) { - QuicCID qcid(cid); - Debug(session, "Updating connection ID %s with reset token", qcid); - session->UpdateConnectionID(type, qcid, StatelessResetToken(token)); - } - return 0; -} - -// Called by ngtcp2 for both client and server connections -// when ngtcp2 has determined that the TLS handshake has -// been completed. It is important to understand that this -// is only an indication of the local peer's handshake state. -// The remote peer might not yet have completed its part -// of the handshake. -int QuicSession::OnHandshakeCompleted( - ngtcp2_conn* conn, - void* user_data) { - - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->HandshakeCompleted(); - return 0; -} - -// Called by ngtcp2 for clients when the handshake has been -// confirmed. Confirmation occurs *after* handshake completion. -int QuicSession::OnHandshakeConfirmed( - ngtcp2_conn* conn, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->HandshakeConfirmed(); - return 0; -} - -// Called by ngtcp2 when a chunk of stream data has been received. -// Currently, ngtcp2 ensures that this callback is always called -// with an offset parameter strictly larger than the previous call's -// offset + datalen (that is, data will never be delivered out of -// order). That behavior may change in the future but only via a -// configuration option. -int QuicSession::OnReceiveStreamData( - ngtcp2_conn* conn, - uint32_t flags, - int64_t stream_id, - uint64_t offset, - const uint8_t* data, - size_t datalen, - void* user_data, - void* stream_user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - return session->ReceiveStreamData( - flags, - stream_id, - data, - datalen, - offset) ? 0 : NGTCP2_ERR_CALLBACK_FAILURE; -} - -// Called by ngtcp2 when a new stream has been opened -int QuicSession::OnStreamOpen( - ngtcp2_conn* conn, - int64_t stream_id, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - // We currently do not do anything with this callback. - // QuicStream instances are created implicitly once the - // first chunk of stream data is received. - - return 0; -} - -// Called by ngtcp2 when an acknowledgement for a chunk of -// TLS handshake data has been received by the remote peer. -// This is only an indication that data was received, not that -// it was successfully processed. Acknowledgements are a key -// part of the QUIC reliability mechanism. -int QuicSession::OnAckedCryptoOffset( - ngtcp2_conn* conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, - uint64_t datalen, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->crypto_context()->AcknowledgeCryptoData(crypto_level, datalen); - return 0; -} - -// Called by ngtcp2 when an acknowledgement for a chunk of -// stream data has been received successfully by the remote peer. -// This is only an indication that data was received, not that -// it was successfully processed. Acknowledgements are a key -// part of the QUIC reliability mechanism. -int QuicSession::OnAckedStreamDataOffset( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t offset, - uint64_t datalen, - void* user_data, - void* stream_user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->AckedStreamDataOffset(stream_id, offset, datalen); - return 0; -} - -// Called by ngtcp2 for a client connection when the server -// has indicated a preferred address in the transport -// params. -// For now, there are two modes: we can accept the preferred address -// or we can reject it. Later, we may want to implement a callback -// to ask the user if they want to accept the preferred address or -// not. -int QuicSession::OnSelectPreferredAddress( - ngtcp2_conn* conn, - ngtcp2_addr* dest, - const ngtcp2_preferred_addr* paddr, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - - // The paddr parameter contains the server advertised preferred - // address. The dest parameter contains the address that is - // actually being used. If the preferred address is selected, - // then the contents of paddr are copied over to dest. It is - // important to remember that SelectPreferredAddress should - // return true regardless of whether the preferred address was - // selected or not. It should only return false if there was - // an actual failure processing things. Note, however, that - // even in such a failure, we debug log and ignore it. - // If the preferred address is not selected, dest remains - // unchanged. - PreferredAddress preferred_address(session->env(), dest, paddr); - session->SelectPreferredAddress(preferred_address); - return 0; -} - -// Called by ngtcp2 when a stream has been closed. -int QuicSession::OnStreamClose( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t app_error_code, - void* user_data, - void* stream_user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->StreamClose(stream_id, app_error_code); - return 0; -} - -// Stream reset means the remote peer will no longer send data -// on the identified stream. It is essentially a premature close. -// The final_size parameter is important here in that it identifies -// exactly how much data the *remote peer* is aware that it sent. -// If there are lost packets, then the local peer's idea of the final -// size might not match. -int QuicSession::OnStreamReset( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t final_size, - uint64_t app_error_code, - void* user_data, - void* stream_user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->StreamReset(stream_id, final_size, app_error_code); - return 0; -} - -// Called by ngtcp2 when it needs to generate some random data. -// We currently do not use it, but the ngtcp2_rand_ctx identifies -// why the random data is necessary. When ctx is equal to -// NGTCP2_RAND_CTX_NONE, it typically means the random data -// is being used during the TLS handshake. When ctx is equal to -// NGTCP2_RAND_CTX_PATH_CHALLENGE, the random data is being -// used to construct a PATH_CHALLENGE. These *might* need more -// secure and robust random number generation given the -// sensitivity of PATH_CHALLENGE operations (an attacker -// could use a compromised PATH_CHALLENGE to trick an endpoint -// into redirecting traffic). -// -// The ngtcp2_rand_ctx tells us what the random data is used for. -// Currently, there is only one use. In the future, we'll want to -// explore whether we want to handle the different cases uses. -int QuicSession::OnRand( - uint8_t* dest, - size_t destlen, - ngtcp2_rand_ctx ctx) { - EntropySource(dest, destlen); - return 0; -} - -// When a new client connection is established, ngtcp2 will call -// this multiple times to generate a pool of connection IDs to use. -int QuicSession::OnGetNewConnectionID( - ngtcp2_conn* conn, - ngtcp2_cid* cid, - uint8_t* token, - size_t cidlen, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - CHECK(!NgCallbackScope::InNgCallbackScope(session)); - session->GetNewConnectionID(cid, token, cidlen); - return 0; -} - -// When a connection is closed, ngtcp2 will call this multiple -// times to retire connection IDs. It's also possible for this -// to be called at times throughout the lifecycle of the connection -// to remove a CID from the availability pool. -int QuicSession::OnRemoveConnectionID( - ngtcp2_conn* conn, - const ngtcp2_cid* cid, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - if (session->is_server()) - session->socket()->DisassociateCID(QuicCID(cid)); - return 0; -} - -// Called by ngtcp2 to perform path validation. Path validation -// is necessary to ensure that a packet is originating from the -// expected source. If the res parameter indicates success, it -// means that the path specified has been verified as being -// valid. -// -// Validity here means only that there has been a successful -// exchange of PATH_CHALLENGE information between the peers. -// It's critical to understand that the validity of a path -// can change at any timee so this is only an indication of -// validity at a specific point in time. -int QuicSession::OnPathValidation( - ngtcp2_conn* conn, - const ngtcp2_path* path, - ngtcp2_path_validation_result res, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->PathValidation(path, res); - return 0; -} - -// Triggered by ngtcp2 when a version negotiation is received. -// What this means is that the remote peer does not support the -// QUIC version requested. The only thing we can do here (per -// the QUIC specification) is silently discard the connection -// and notify the JavaScript side that a different version of -// QUIC should be used. The sv parameter does list the QUIC -// versions advertised as supported by the remote peer. -int QuicSession::OnVersionNegotiation( - ngtcp2_conn* conn, - const ngtcp2_pkt_hd* hd, - const uint32_t* sv, - size_t nsv, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - QuicSession::NgCallbackScope callback_scope(session); - session->VersionNegotiation(sv, nsv); - return 0; -} - -// Triggered by ngtcp2 when a stateless reset is received. What this -// means is that the remote peer might recognize the CID but has lost -// all state necessary to successfully process it. The only thing we -// can do is silently close the connection. For server sessions, this -// means all session state is shut down and discarded, even on the -// JavaScript side. For client sessions, we discard session state at -// the C++ layer but -- at least in the future -- we can retain some -// state at the JavaScript level to allow for automatic session -// resumption. -int QuicSession::OnStatelessReset( - ngtcp2_conn* conn, - const ngtcp2_pkt_stateless_reset* sr, - void* user_data) { - QuicSession* session = static_cast<QuicSession*>(user_data); - - if (UNLIKELY(session->is_destroyed())) - return NGTCP2_ERR_CALLBACK_FAILURE; - - session->set_stateless_reset(); - return 0; -} - -const ngtcp2_conn_callbacks QuicSession::callbacks[2] = { - // NGTCP2_CRYPTO_SIDE_CLIENT - { - ngtcp2_crypto_client_initial_cb, - nullptr, - OnReceiveCryptoData, - OnHandshakeCompleted, - OnVersionNegotiation, - ngtcp2_crypto_encrypt_cb, - ngtcp2_crypto_decrypt_cb, - ngtcp2_crypto_hp_mask_cb, - OnReceiveStreamData, - OnAckedCryptoOffset, - OnAckedStreamDataOffset, - OnStreamOpen, - OnStreamClose, - OnStatelessReset, - ngtcp2_crypto_recv_retry_cb, - OnExtendMaxStreamsBidi, - OnExtendMaxStreamsUni, - OnRand, - OnGetNewConnectionID, - OnRemoveConnectionID, - ngtcp2_crypto_update_key_cb, - OnPathValidation, - OnSelectPreferredAddress, - OnStreamReset, - OnExtendMaxStreamsRemoteBidi, - OnExtendMaxStreamsRemoteUni, - OnExtendMaxStreamData, - OnConnectionIDStatus, - OnHandshakeConfirmed, - nullptr, // recv_new_token - ngtcp2_crypto_delete_crypto_aead_ctx_cb, - ngtcp2_crypto_delete_crypto_cipher_ctx_cb, - }, - // NGTCP2_CRYPTO_SIDE_SERVER - { - nullptr, - ngtcp2_crypto_recv_client_initial_cb, - OnReceiveCryptoData, - OnHandshakeCompleted, - nullptr, // recv_version_negotiation - ngtcp2_crypto_encrypt_cb, - ngtcp2_crypto_decrypt_cb, - ngtcp2_crypto_hp_mask_cb, - OnReceiveStreamData, - OnAckedCryptoOffset, - OnAckedStreamDataOffset, - OnStreamOpen, - OnStreamClose, - OnStatelessReset, - nullptr, // recv_retry - nullptr, // extend_max_streams_bidi - nullptr, // extend_max_streams_uni - OnRand, - OnGetNewConnectionID, - OnRemoveConnectionID, - ngtcp2_crypto_update_key_cb, - OnPathValidation, - nullptr, // select_preferred_addr - OnStreamReset, - OnExtendMaxStreamsRemoteBidi, - OnExtendMaxStreamsRemoteUni, - OnExtendMaxStreamData, - OnConnectionIDStatus, - nullptr, // handshake_confirmed - nullptr, // recv_new_token - ngtcp2_crypto_delete_crypto_aead_ctx_cb, - ngtcp2_crypto_delete_crypto_cipher_ctx_cb, - } -}; - -BaseObjectPtr<QLogStream> QuicSession::qlog_stream() { - if (!qlog_stream_) { - CHECK(qlog_stream_ = QLogStream::Create(env())); - listener_->OnQLog(qlog_stream_.get()); - } - return qlog_stream_; -} - -void QuicSession::OnQlogWrite( - void* user_data, - uint32_t flags, - const void* data, - size_t len) { - QuicSession* session = static_cast<QuicSession*>(user_data); - Environment* env = session->env(); - - // Fun fact... ngtcp2 does not emit the final qlog statement until the - // ngtcp2_conn object is destroyed. Ideally, destroying is explicit, - // but sometimes the QuicSession object can be garbage collected without - // being explicitly destroyed. During those times, we cannot call out - // to JavaScript. Because we don't know for sure if we're in in a GC - // when this is called, it is safer to just defer writes to immediate. - BaseObjectPtr<QLogStream> ptr = session->qlog_stream(); - std::vector<uint8_t> buffer(len); - memcpy(buffer.data(), data, len); - env->SetImmediate([ptr = std::move(ptr), - buffer = std::move(buffer), - flags](Environment*) { - ptr->Emit(buffer.data(), buffer.size(), flags); - }); -} - -BaseObjectPtr<QLogStream> QLogStream::Create(Environment* env) { - HandleScope scope(env->isolate()); - - // TODO(@jasnell): There is identical code in heap_utils for the - // HeapSnapshotStream. We can consolidate the two. - if (env->qlogoutputstream_constructor_template().IsEmpty()) { - // Create FunctionTemplate for QLogStream - Local<FunctionTemplate> os = FunctionTemplate::New(env->isolate()); - os->Inherit(AsyncWrap::GetConstructorTemplate(env)); - Local<ObjectTemplate> ost = os->InstanceTemplate(); - ost->SetInternalFieldCount(StreamBase::kInternalFieldCount); - os->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "QLogStream")); - StreamBase::AddMethods(env, os); - env->set_qlogoutputstream_constructor_template(ost); - } - - Local<Object> obj; - if (!env->qlogoutputstream_constructor_template() - ->NewInstance(env->context()) - .ToLocal(&obj)) { - return {}; - } - - return MakeBaseObject<QLogStream>(env, obj); -} - -QLogStream::QLogStream(Environment* env, v8::Local<Object> obj) - : AsyncWrap(env, obj, AsyncWrap::PROVIDER_QLOGSTREAM), - StreamBase(env) { - MakeWeak(); - StreamBase::AttachToObject(GetObject()); -} - -void QLogStream::Emit(const uint8_t* data, size_t len, uint32_t flags) { - size_t remaining = len; - while (remaining != 0) { - uv_buf_t buf = EmitAlloc(len); - ssize_t avail = std::min<size_t>(remaining, buf.len); - memcpy(buf.base, data, avail); - remaining -= avail; - data += avail; - EmitRead(avail, buf); - } - - if (ended_ && flags & NGTCP2_QLOG_WRITE_FLAG_FIN) - EmitRead(UV_EOF); -} - -int QLogStream::DoShutdown(ShutdownWrap* req_wrap) { - UNREACHABLE(); -} - -int QLogStream::DoWrite( - WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle) { - UNREACHABLE(); -} - -bool QLogStream::IsAlive() { return true; } -bool QLogStream::IsClosing() { return false; } - -// JavaScript API - -namespace { -void QuicSessionSetSocket(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - CHECK(args[0]->IsObject()); - ASSIGN_OR_RETURN_UNWRAP(&socket, args[0].As<Object>()); - args.GetReturnValue().Set(session->set_socket(socket, args[1]->IsTrue())); -} - -// GracefulClose flips a flag that prevents new local streams -// from being opened and new remote streams from being received. It is -// important to note that this does *NOT* send a CONNECTION_CLOSE packet -// to the peer. Existing streams are permitted to close gracefully. -void QuicSessionGracefulClose(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - session->StartGracefulClose(); -} - -// Destroying the QuicSession will trigger sending of a CONNECTION_CLOSE -// packet, after which the QuicSession will be immediately torn down. -void QuicSessionDestroy(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - session->set_last_error(QuicError(env, args[0], args[1])); - session->Destroy(); -} - -void QuicSessionGetEphemeralKeyInfo(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - Local<Object> ret; - if (session->crypto_context()->ephemeral_key().ToLocal(&ret)) - args.GetReturnValue().Set(ret); -} - -void QuicSessionGetPeerCertificate(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - Local<Value> ret; - if (session->crypto_context()->peer_cert(!args[0]->IsTrue()).ToLocal(&ret)) - args.GetReturnValue().Set(ret); -} - -void QuicSessionGetRemoteAddress( - const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - Environment* env = session->env(); - CHECK(args[0]->IsObject()); - args.GetReturnValue().Set( - session->remote_address().ToJS(env, args[0].As<Object>())); -} - -void QuicSessionGetCertificate( - const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - Local<Value> ret; - if (session->crypto_context()->cert().ToLocal(&ret)) - args.GetReturnValue().Set(ret); -} - -void QuicSessionPing(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - session->Ping(); -} - -// Triggers a silent close of a QuicSession. This is currently only used -// (and should ever only be used) for testing purposes... -void QuicSessionSilentClose(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As<Object>()); - ProcessEmitWarning( - session->env(), - "Forcing silent close of QuicSession for testing purposes only"); - session->Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); -} - -// This is used purely for testing. -void QuicSessionRemoveFromSocket(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - session->RemoveFromSocket(); -} - -void QuicSessionUpdateKey(const FunctionCallbackInfo<Value>& args) { - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - // Initiating a key update may fail if it is done too early (either - // before the TLS handshake has been confirmed or while a previous - // key update is being processed). When it fails, InitiateKeyUpdate() - // will return false. - args.GetReturnValue().Set(session->crypto_context()->InitiateKeyUpdate()); -} - -// When a client wishes to resume a prior TLS session, it must specify both -// the remember transport parameters and remembered TLS session ticket. Those -// will each be provided as a TypedArray. The DecodeTransportParams and -// DecodeSessionTicket functions handle those. If the argument is undefined, -// then resumption is not used. - -bool DecodeTransportParams( - Local<Value> value, - ngtcp2_transport_params* params) { - if (value->IsUndefined()) - return false; - CHECK(value->IsArrayBufferView()); - ArrayBufferViewContents<uint8_t> sbuf(value.As<ArrayBufferView>()); - if (sbuf.length() != sizeof(ngtcp2_transport_params)) - return false; - memcpy(params, sbuf.data(), sizeof(ngtcp2_transport_params)); - return true; -} - -crypto::SSLSessionPointer DecodeSessionTicket(Local<Value> value) { - if (value->IsUndefined()) - return {}; - CHECK(value->IsArrayBufferView()); - ArrayBufferViewContents<unsigned char> sbuf(value.As<ArrayBufferView>()); - return crypto::GetTLSSession(sbuf.data(), sbuf.length()); -} - -void NewQuicClientSession(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - - QuicSocket* socket; - int32_t family; - uint32_t port; - SecureContext* sc; - SocketAddress remote_addr; - int32_t preferred_address_policy; - PreferredAddressStrategy preferred_address_strategy; - uint32_t options = QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY; - std::string alpn(NGHTTP3_ALPN_H3); - - enum ARG_IDX : int { - SOCKET, - TYPE, - IP, - PORT, - SECURE_CONTEXT, - SNI, - REMOTE_TRANSPORT_PARAMS, - SESSION_TICKET, - DCID, - PREFERRED_ADDRESS_POLICY, - ALPN, - OPTIONS, - QLOG - }; - - CHECK(args[ARG_IDX::SOCKET]->IsObject()); - CHECK(args[ARG_IDX::SECURE_CONTEXT]->IsObject()); - CHECK(args[ARG_IDX::IP]->IsString()); - CHECK(args[ARG_IDX::ALPN]->IsString()); - CHECK(args[ARG_IDX::TYPE]->Int32Value(env->context()).To(&family)); - CHECK(args[ARG_IDX::PORT]->Uint32Value(env->context()).To(&port)); - CHECK(args[ARG_IDX::OPTIONS]->Uint32Value(env->context()).To(&options)); - if (!args[ARG_IDX::SNI]->IsUndefined()) - CHECK(args[ARG_IDX::SNI]->IsString()); - - ASSIGN_OR_RETURN_UNWRAP(&socket, args[ARG_IDX::SOCKET].As<Object>()); - ASSIGN_OR_RETURN_UNWRAP(&sc, args[ARG_IDX::SECURE_CONTEXT].As<Object>()); - - CHECK(args[ARG_IDX::PREFERRED_ADDRESS_POLICY]->Int32Value( - env->context()).To(&preferred_address_policy)); - switch (preferred_address_policy) { - case QUIC_PREFERRED_ADDRESS_USE: - preferred_address_strategy = QuicSession::UsePreferredAddressStrategy; - break; - default: - preferred_address_strategy = QuicSession::IgnorePreferredAddressStrategy; - } - - node::Utf8Value address(env->isolate(), args[ARG_IDX::IP]); - node::Utf8Value servername(env->isolate(), args[ARG_IDX::SNI]); - - if (!SocketAddress::New(family, *address, port, &remote_addr)) - return args.GetReturnValue().Set(ERR_FAILED_TO_CREATE_SESSION); - - // ALPN is a string prefixed by the length, followed by values - Utf8Value val(env->isolate(), args[ARG_IDX::ALPN]); - alpn = val.length(); - alpn += *val; - - crypto::SSLSessionPointer early_session_ticket = - DecodeSessionTicket(args[ARG_IDX::SESSION_TICKET]); - ngtcp2_transport_params early_transport_params; - bool has_early_transport_params = - DecodeTransportParams( - args[ARG_IDX::REMOTE_TRANSPORT_PARAMS], - &early_transport_params); - - socket->ReceiveStart(); - - BaseObjectPtr<QuicSession> session = - QuicSession::CreateClient( - socket, - socket->local_address(), - remote_addr, - BaseObjectPtr<SecureContext>(sc), - has_early_transport_params ? &early_transport_params : nullptr, - std::move(early_session_ticket), - args[ARG_IDX::DCID], - preferred_address_strategy, - alpn, - std::string(*servername), - options, - args[ARG_IDX::QLOG]->IsTrue() ? - QlogMode::kEnabled : - QlogMode::kDisabled); - session->SendPendingData(); - if (session->is_destroyed()) - return args.GetReturnValue().Set(ERR_FAILED_TO_CREATE_SESSION); - - args.GetReturnValue().Set(session->object()); -} - -// Add methods that are shared by both client and server QuicSessions -void AddMethods(Environment* env, Local<FunctionTemplate> session) { - env->SetProtoMethod(session, "destroy", QuicSessionDestroy); - env->SetProtoMethod(session, "getRemoteAddress", QuicSessionGetRemoteAddress); - env->SetProtoMethod(session, "getCertificate", QuicSessionGetCertificate); - env->SetProtoMethod(session, "getPeerCertificate", - QuicSessionGetPeerCertificate); - env->SetProtoMethod(session, "gracefulClose", QuicSessionGracefulClose); - env->SetProtoMethod(session, "updateKey", QuicSessionUpdateKey); - env->SetProtoMethod(session, "ping", QuicSessionPing); - env->SetProtoMethod(session, "removeFromSocket", QuicSessionRemoveFromSocket); - env->SetProtoMethod(session, "onClientHelloDone", - QuicSessionOnClientHelloDone); - env->SetProtoMethod(session, "onCertDone", QuicSessionOnCertDone); -} -} // namespace - -void QuicSession::Initialize( - Environment* env, - Local<Object> target, - Local<Context> context) { - { - Local<String> class_name = - FIXED_ONE_BYTE_STRING(env->isolate(), "QuicServerSession"); - Local<FunctionTemplate> session = FunctionTemplate::New(env->isolate()); - session->SetClassName(class_name); - session->Inherit(AsyncWrap::GetConstructorTemplate(env)); - Local<ObjectTemplate> sessiont = session->InstanceTemplate(); - sessiont->SetInternalFieldCount(QuicSession::kInternalFieldCount); - sessiont->Set(env->owner_symbol(), Null(env->isolate())); - AddMethods(env, session); - env->set_quicserversession_instance_template(sessiont); - } - - { - Local<String> class_name = - FIXED_ONE_BYTE_STRING(env->isolate(), "QuicClientSession"); - Local<FunctionTemplate> session = FunctionTemplate::New(env->isolate()); - session->SetClassName(class_name); - session->Inherit(AsyncWrap::GetConstructorTemplate(env)); - Local<ObjectTemplate> sessiont = session->InstanceTemplate(); - sessiont->SetInternalFieldCount(QuicSession::kInternalFieldCount); - sessiont->Set(env->owner_symbol(), Null(env->isolate())); - AddMethods(env, session); - env->SetProtoMethod(session, - "getEphemeralKeyInfo", - QuicSessionGetEphemeralKeyInfo); - env->SetProtoMethod(session, - "setSocket", - QuicSessionSetSocket); - env->set_quicclientsession_instance_template(sessiont); - - env->SetMethod(target, "createClientSession", NewQuicClientSession); - env->SetMethod(target, "silentCloseSession", QuicSessionSilentClose); - } -} - -} // namespace quic -} // namespace node diff --git a/src/quic/node_quic_session.h b/src/quic/node_quic_session.h deleted file mode 100644 index 10e16cc2689..00000000000 --- a/src/quic/node_quic_session.h +++ /dev/null @@ -1,1531 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_SESSION_H_ -#define SRC_QUIC_NODE_QUIC_SESSION_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "aliased_buffer.h" -#include "aliased_struct.h" -#include "async_wrap.h" -#include "env.h" -#include "handle_wrap.h" -#include "node.h" -#include "node_crypto.h" -#include "node_http_common.h" -#include "node_mem.h" -#include "node_quic_state.h" -#include "node_quic_buffer-inl.h" -#include "node_quic_crypto.h" -#include "node_quic_util.h" -#include "node_sockaddr.h" -#include "stream_base.h" -#include "timer_wrap.h" -#include "v8.h" -#include "uv.h" - -#include <ngtcp2/ngtcp2.h> -#include <ngtcp2/ngtcp2_crypto.h> -#include <nghttp3/nghttp3.h> -#include <openssl/ssl.h> - -#include <unordered_map> -#include <string> -#include <vector> - -namespace node { -namespace quic { - -using ConnectionPointer = DeleteFnPtr<ngtcp2_conn, ngtcp2_conn_del>; - -class QuicApplication; -class QuicPacket; -class QuicSocket; -class QuicStream; - -using QuicHeader = NgHeaderBase<QuicApplication>; - -using StreamsMap = std::unordered_map<int64_t, BaseObjectPtr<QuicStream>>; - -enum class QlogMode { - kDisabled, - kEnabled -}; - -typedef void(*ConnectionIDStrategy)( - QuicSession* session, - ngtcp2_cid* cid, - size_t cidlen); - -typedef void(*PreferredAddressStrategy)( - QuicSession* session, - const PreferredAddress& preferred_address); - -// The QuicSessionConfig class holds the initial transport parameters and -// configuration options set by the JavaScript side when either a -// client or server QuicSession is created. Instances are -// stack created and use a combination of an AliasedBuffer to pass -// the numeric settings quickly (see node_quic_state.h) and passed -// in non-numeric settings (e.g. preferred_addr). -class QuicSessionConfig final : public ngtcp2_settings { - public: - QuicSessionConfig() = default; - - explicit QuicSessionConfig(QuicState* quic_state) { - Set(quic_state); - } - - QuicSessionConfig(const QuicSessionConfig& config) { - initial_ts = uv_hrtime(); - initial_rtt = config.initial_rtt; - transport_params = config.transport_params; - max_udp_payload_size = config.max_udp_payload_size; - cc_algo = config.cc_algo; - cc = config.cc; - qlog = config.qlog; - log_printf = config.log_printf; - token = config.token; - } - - void ResetToDefaults(QuicState* quic_state); - - // QuicSessionConfig::Set() pulls values out of the AliasedBuffer - // defined in node_quic_state.h and stores the values in settings_. - // If preferred_addr is not nullptr, it is copied into the - // settings_.preferred_addr field - void Set(QuicState* quic_state, - const struct sockaddr* preferred_addr = nullptr); - - inline void set_original_connection_id( - const QuicCID& ocid, - const QuicCID& scid); - - // Generates the stateless reset token for the settings_ - inline void GenerateStatelessResetToken( - QuicSession* session, - const QuicCID& cid); - - // If the preferred address is set, generates the associated tokens - inline void GeneratePreferredAddressToken( - ConnectionIDStrategy connection_id_strategy, - QuicSession* session, - QuicCID* pscid); - - inline void set_qlog(const ngtcp2_qlog_settings& qlog); -}; - -// Options to alter the behavior of various functions on the -// server QuicSession. These are set on the QuicSocket when -// the listen() function is called and are passed to the -// constructor of the server QuicSession. -enum QuicServerSessionOptions : uint32_t { - // When set, instructs the server QuicSession to reject - // client authentication certs that cannot be verified. - QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED = 0x1, - - // When set, instructs the server QuicSession to request - // a client authentication cert - QUICSERVERSESSION_OPTION_REQUEST_CERT = 0x2 -}; - -// Options to alter the behavior of various functions on the -// client QuicSession. These are set on the client QuicSession -// constructor. -enum QuicClientSessionOptions : uint32_t { - // When set, instructs the client QuicSession to include an - // OCSP request in the initial TLS handshake - QUICCLIENTSESSION_OPTION_REQUEST_OCSP = 0x1, - - // When set, instructs the client QuicSession to verify the - // hostname identity. This is required by QUIC and enabled - // by default. We allow disabling it only for debugging - // purposes. - QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY = 0x2, - - // When set, instructs the client QuicSession to perform - // additional checks on TLS session resumption. - QUICCLIENTSESSION_OPTION_RESUME = 0x4 -}; - -#define QUICSESSION_SHARED_STATE(V) \ - V(KEYLOG_ENABLED, keylog_enabled, uint8_t) \ - V(CLIENT_HELLO_ENABLED, client_hello_enabled, uint8_t) \ - V(OCSP_ENABLED, ocsp_enabled, uint8_t) \ - V(PATH_VALIDATED_ENABLED, path_validated_enabled, uint8_t) \ - V(USE_PREFERRED_ADDRESS_ENABLED, use_preferred_address_enabled, uint8_t) \ - V(HANDSHAKE_CONFIRMED, handshake_confirmed, uint8_t) \ - V(IDLE_TIMEOUT, idle_timeout, uint8_t) \ - V(MAX_STREAMS_BIDI, max_streams_bidi, uint64_t) \ - V(MAX_STREAMS_UNI, max_streams_uni, uint64_t) \ - V(MAX_DATA_LEFT, max_data_left, uint64_t) \ - V(BYTES_IN_FLIGHT, bytes_in_flight, uint64_t) - -#define V(_, name, type) type name; -struct QuicSessionState { - QUICSESSION_SHARED_STATE(V) -}; -#undef V - -#define V(id, name, _) \ - IDX_QUICSESSION_STATE_##id = offsetof(QuicSessionState, name), -enum QuicSessionStateFields { - QUICSESSION_SHARED_STATE(V) - IDX_QUICSESSION_STATE_END -}; -#undef V - -#define SESSION_STATS(V) \ - V(CREATED_AT, created_at, "Created At") \ - V(HANDSHAKE_START_AT, handshake_start_at, "Handshake Started") \ - V(HANDSHAKE_SEND_AT, handshake_send_at, "Handshke Last Sent") \ - V(HANDSHAKE_CONTINUE_AT, handshake_continue_at, "Handshke Continued") \ - V(HANDSHAKE_COMPLETED_AT, handshake_completed_at, "Handshake Completed") \ - V(HANDSHAKE_CONFIRMED_AT, handshake_confirmed_at, "Handshake Confirmed") \ - V(HANDSHAKE_ACKED_AT, handshake_acked_at, "Handshake Last Acknowledged") \ - V(SENT_AT, sent_at, "Last Sent At") \ - V(RECEIVED_AT, received_at, "Last Received At") \ - V(CLOSING_AT, closing_at, "Closing") \ - V(DESTROYED_AT, destroyed_at, "Destroyed At") \ - V(BYTES_RECEIVED, bytes_received, "Bytes Received") \ - V(BYTES_SENT, bytes_sent, "Bytes Sent") \ - V(BIDI_STREAM_COUNT, bidi_stream_count, "Bidi Stream Count") \ - V(UNI_STREAM_COUNT, uni_stream_count, "Uni Stream Count") \ - V(STREAMS_IN_COUNT, streams_in_count, "Streams In Count") \ - V(STREAMS_OUT_COUNT, streams_out_count, "Streams Out Count") \ - V(KEYUPDATE_COUNT, keyupdate_count, "Key Update Count") \ - V(LOSS_RETRANSMIT_COUNT, loss_retransmit_count, "Loss Retransmit Count") \ - V(ACK_DELAY_RETRANSMIT_COUNT, \ - ack_delay_retransmit_count, \ - "Ack Delay Retransmit Count") \ - V(PATH_VALIDATION_SUCCESS_COUNT, \ - path_validation_success_count, \ - "Path Validation Success Count") \ - V(PATH_VALIDATION_FAILURE_COUNT, \ - path_validation_failure_count, \ - "Path Validation Failure Count") \ - V(MAX_BYTES_IN_FLIGHT, max_bytes_in_flight, "Max Bytes In Flight") \ - V(BLOCK_COUNT, block_count, "Block Count") \ - V(MIN_RTT, min_rtt, "Minimum RTT") \ - V(LATEST_RTT, latest_rtt, "Latest RTT") \ - V(SMOOTHED_RTT, smoothed_rtt, "Smoothed RTT") \ - V(CWND, cwnd, "Cwnd") \ - V(RECEIVE_RATE, receive_rate, "Receive Rate / Sec") \ - V(SEND_RATE, send_rate, "Send Rate Sec") \ - -#define V(name, _, __) IDX_QUIC_SESSION_STATS_##name, -enum QuicSessionStatsIdx : int { - SESSION_STATS(V) - IDX_QUIC_SESSION_STATS_COUNT -}; -#undef V - -#define V(_, name, __) uint64_t name; -struct QuicSessionStats { - SESSION_STATS(V) -}; -#undef V - -struct QuicSessionStatsTraits { - using Stats = QuicSessionStats; - using Base = QuicSession; - - template <typename Fn> - static void ToString(const Base& ptr, Fn&& add_field); -}; - -class QLogStream final : public AsyncWrap, - public StreamBase { - public: - static BaseObjectPtr<QLogStream> Create(Environment* env); - - QLogStream(Environment* env, v8::Local<v8::Object> obj); - - void Emit(const uint8_t* data, size_t len, uint32_t flags); - - void End() { ended_ = true; } - - int ReadStart() override { return 0; } - int ReadStop() override { return 0; } - int DoShutdown(ShutdownWrap* req_wrap) override; - int DoWrite(WriteWrap* w, - uv_buf_t* bufs, - size_t count, - uv_stream_t* send_handle) override; - bool IsAlive() override; - bool IsClosing() override; - AsyncWrap* GetAsyncWrap() override { return this; } - - SET_NO_MEMORY_INFO(); - SET_MEMORY_INFO_NAME(QLogStream); - SET_SELF_SIZE(QLogStream); - - private: - bool ended_ = false; -}; - -class QuicSessionListener { - public: - enum SessionCloseFlags { - SESSION_CLOSE_FLAG_NONE, - SESSION_CLOSE_FLAG_SILENT, - SESSION_CLOSE_FLAG_STATELESS_RESET - }; - - virtual ~QuicSessionListener(); - - virtual void OnKeylog(const char* str, size_t size); - virtual void OnClientHello( - const char* alpn, - const char* server_name); - virtual void OnCert(const char* server_name); - virtual void OnOCSP(v8::Local<v8::Value> ocsp); - virtual void OnStreamHeaders( - int64_t stream_id, - int kind, - const std::vector<std::unique_ptr<QuicHeader>>& headers, - int64_t push_id); - virtual void OnStreamClose( - int64_t stream_id, - uint64_t app_error_code); - virtual void OnStreamReset( - int64_t stream_id, - uint64_t app_error_code); - virtual void OnSessionClose( - QuicError error, - int flags = SESSION_CLOSE_FLAG_NONE); - virtual void OnStreamReady(BaseObjectPtr<QuicStream> stream); - virtual void OnHandshakeCompleted(); - virtual void OnPathValidation( - ngtcp2_path_validation_result res, - const sockaddr* local, - const sockaddr* remote); - virtual void OnUsePreferredAddress( - int family, - const PreferredAddress& preferred_address); - virtual void OnSessionTicket(int size, SSL_SESSION* session); - virtual void OnStreamBlocked(int64_t stream_id); - virtual void OnVersionNegotiation( - uint32_t supported_version, - const uint32_t* versions, - size_t vcnt); - virtual void OnQLog(QLogStream* qlog_stream); - - QuicSession* session() const { return session_.get(); } - - private: - BaseObjectWeakPtr<QuicSession> session_; - QuicSessionListener* previous_listener_ = nullptr; - friend class QuicSession; -}; - -class JSQuicSessionListener final : public QuicSessionListener { - public: - void OnKeylog(const char* str, size_t size) override; - void OnClientHello( - const char* alpn, - const char* server_name) override; - void OnCert(const char* server_name) override; - void OnOCSP(v8::Local<v8::Value> ocsp) override; - void OnStreamHeaders( - int64_t stream_id, - int kind, - const std::vector<std::unique_ptr<QuicHeader>>& headers, - int64_t push_id) override; - void OnStreamClose( - int64_t stream_id, - uint64_t app_error_code) override; - void OnStreamReset( - int64_t stream_id, - uint64_t app_error_code) override; - void OnSessionClose( - QuicError error, - int flags = SESSION_CLOSE_FLAG_NONE) override; - void OnStreamReady(BaseObjectPtr<QuicStream> stream) override; - void OnHandshakeCompleted() override; - void OnPathValidation( - ngtcp2_path_validation_result res, - const sockaddr* local, - const sockaddr* remote) override; - void OnSessionTicket(int size, SSL_SESSION* session) override; - void OnUsePreferredAddress( - int family, - const PreferredAddress& preferred_address) override; - void OnStreamBlocked(int64_t stream_id) override; - void OnVersionNegotiation( - uint32_t supported_version, - const uint32_t* versions, - size_t vcnt) override; - void OnQLog(QLogStream* qlog_stream) override; - - private: - friend class QuicSession; -}; - -#define QUICCRYPTOCONTEXT_FLAGS(V) \ - V(IN_TLS_CALLBACK, in_tls_callback) \ - V(IN_KEY_UPDATE, in_key_update) \ - V(IN_OCSP_RESPONSE, in_ocsp_request) \ - V(IN_CLIENT_HELLO, in_client_hello) \ - V(EARLY_DATA, early_data) - -// The QuicCryptoContext class encapsulates all of the crypto/TLS -// handshake details on behalf of a QuicSession. -class QuicCryptoContext final : public MemoryRetainer { - public: - inline QuicCryptoContext( - QuicSession* session, - BaseObjectPtr<crypto::SecureContext> secure_context, - ngtcp2_crypto_side side, - uint32_t options); - - ~QuicCryptoContext() override; - - inline uint64_t Cancel(); - - // Outgoing crypto data must be retained in memory until it is - // explicitly acknowledged. AcknowledgeCryptoData will be invoked - // when ngtcp2 determines that it has received an acknowledgement - // for crypto data at the specified level. This is our indication - // that the data for that level can be released. - void AcknowledgeCryptoData(ngtcp2_crypto_level level, uint64_t datalen); - - inline void Initialize(); - - // Enables openssl's TLS tracing mechanism for this session only. - void EnableTrace(); - - // Returns the server's prepared OCSP response for transmission. This - // is not used by client QuicSession instances. - inline v8::MaybeLocal<v8::Value> ocsp_response() const; - - // Returns ngtcp2's understanding of the current inbound crypto level - inline ngtcp2_crypto_level read_crypto_level() const; - - // Returns ngtcp2's understanding of the current outbound crypto level - inline ngtcp2_crypto_level write_crypto_level() const; - - inline bool early_data() const; - - bool is_option_set(uint32_t option) const { return options_ & option; } - - // Emits a single keylog line to the JavaScript layer - inline void Keylog(const char* line); - - int OnClientHello(); - - void OnClientHelloDone(BaseObjectPtr<crypto::SecureContext> context); - - int OnOCSP(); - - void OnOCSPDone(v8::Local<v8::Value> ocsp_response); - - bool OnSecrets( - ngtcp2_crypto_level level, - const uint8_t* rx_secret, - const uint8_t* tx_secret, - size_t secretlen); - - int OnTLSStatus(); - - // Receives and processes TLS handshake details - int Receive( - ngtcp2_crypto_level crypto_level, - uint64_t offset, - const uint8_t* data, - size_t datalen); - - // Resumes the TLS handshake following a client hello or - // OCSP callback - inline void ResumeHandshake(); - - inline v8::MaybeLocal<v8::Value> cert() const; - inline v8::MaybeLocal<v8::Value> cipher_name() const; - inline v8::MaybeLocal<v8::Value> cipher_version() const; - inline v8::MaybeLocal<v8::Object> ephemeral_key() const; - inline const char* hello_alpn() const; - inline v8::MaybeLocal<v8::Array> hello_ciphers() const; - inline const char* hello_servername() const; - inline v8::MaybeLocal<v8::Value> peer_cert(bool abbreviated) const; - inline std::string selected_alpn() const; - inline const char* servername() const; - - void set_option(uint32_t option, bool on = true) { - if (on) - options_ |= option; - else - options_ &= ~option; - } - -#define V(id, name) \ - inline bool is_##name() const { \ - return flags_ & (1 << QUICCRYPTOCONTEXT_FLAG_##id); } \ - inline void set_##name(bool on = true) { \ - if (on) \ - flags_ |= (1 << QUICCRYPTOCONTEXT_FLAG_##id); \ - else \ - flags_ &= ~(1 << QUICCRYPTOCONTEXT_FLAG_##id); \ - } - QUICCRYPTOCONTEXT_FLAGS(V) -#undef V - - inline bool set_session(crypto::SSLSessionPointer session); - - inline void set_tls_alert(int err); - - ngtcp2_crypto_side side() const { return side_; } - - void WriteHandshake( - ngtcp2_crypto_level level, - const uint8_t* data, - size_t datalen); - - bool InitiateKeyUpdate(); - - int VerifyPeerIdentity(); - - QuicSession* session() const { return session_.get(); } - - void MemoryInfo(MemoryTracker* tracker) const override; - - SET_MEMORY_INFO_NAME(QuicCryptoContext) - SET_SELF_SIZE(QuicCryptoContext) - - private: - bool SetSecrets( - ngtcp2_crypto_level level, - const uint8_t* rx_secret, - const uint8_t* tx_secret, - size_t secretlen); - - BaseObjectWeakPtr<QuicSession> session_; - BaseObjectPtr<crypto::SecureContext> secure_context_; - ngtcp2_crypto_side side_; - crypto::SSLPointer ssl_; - QuicBuffer handshake_[3]; - uint32_t options_; - uint32_t flags_ = 0; - - v8::Global<v8::ArrayBufferView> ocsp_response_; - crypto::BIOPointer bio_trace_; - -#define V(id, _) QUICCRYPTOCONTEXT_FLAG_##id, - enum QuicCryptoContextFlags : uint32_t { - QUICCRYPTOCONTEXT_FLAGS(V) - QUICCRYPTOCONTEXT_FLAG_COUNT - }; -#undef V - - class TLSCallbackScope { - public: - explicit TLSCallbackScope(QuicCryptoContext* context) : - context_(context) { - context_->set_in_tls_callback(); - } - - ~TLSCallbackScope() { - context_->set_in_tls_callback(false); - } - - static bool is_in_callback(QuicCryptoContext* context) { - return context->is_in_tls_callback(); - } - - private: - QuicCryptoContext* context_; - }; - - class TLSHandshakeScope { - public: - using DoneCB = std::function<void()>; - TLSHandshakeScope( - QuicCryptoContext* context, - DoneCB done) : - context_(context), - done_(done) {} - - ~TLSHandshakeScope() { - if (!is_handshake_suspended()) - return; - - done_(); - // Only continue the TLS handshake if we are not currently running - // synchronously within the TLS handshake function. This can happen - // when the callback function passed to the clientHello and cert - // event handlers is called synchronously. If the function is called - // asynchronously, then we have to manually continue the handshake. - if (!TLSCallbackScope::is_in_callback(context_)) - context_->ResumeHandshake(); - } - - private: - bool is_handshake_suspended() const { - return context_->is_in_ocsp_request() || context_->is_in_client_hello(); - } - - - QuicCryptoContext* context_; - DoneCB done_; - }; - - friend class QuicSession; -}; - -// A QuicApplication encapsulates the specific details of -// working with a specific QUIC application (e.g. http/3). -class QuicApplication : public MemoryRetainer, - public mem::NgLibMemoryManagerBase { - public: - inline explicit QuicApplication(QuicSession* session); - virtual ~QuicApplication() = default; - - // The QuicSession will call Initialize as soon as the TLS - // secrets have been set. See QuicCryptoContext::OnSecrets - virtual bool Initialize() = 0; - - // QuicSession will forward all received stream data immediately - // on to the QuicApplication. The only additional processing the - // QuicSession does is to automatically adjust the QuicSession-level - // flow control window. It is up to the QuicApplication to do - // the same for the QuicStream-level flow control. - // - // flags are passed on directly from ngtcp2. The most important - // of which here is NGTCP2_STREAM_DATA_FLAG_FIN, which indicates - // that this is the final chunk of data that the peer will send - // for this stream. - // - // It is also possible for the NGTCP2_STREAM_DATA_FLAG_0RTT flag - // to be set, indicating that this chunk of data was received in - // a 0RTT packet before the TLS handshake completed. This would - // indicate that it is not as secure and could be replayed by - // an attacker. We're not currently making use of that flag. - virtual bool ReceiveStreamData( - uint32_t flags, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - uint64_t offset) = 0; - - virtual void AcknowledgeStreamData( - int64_t stream_id, - uint64_t offset, - size_t datalen) { - Acknowledge(stream_id, offset, datalen); - } - - virtual bool BlockStream(int64_t id) { return true; } - - virtual void ExtendMaxStreamsRemoteUni(uint64_t max_streams) {} - - virtual void ExtendMaxStreamsRemoteBidi(uint64_t max_streams) {} - - virtual void ExtendMaxStreamData(int64_t stream_id, uint64_t max_data) {} - - virtual void ResumeStream(int64_t stream_id) {} - - // Different QUIC applications may set some application data in - // the session ticket (e.g. http/3 would set server settings in the - // application data). By default, there's nothing to set. - virtual void SetSessionTicketAppData(const SessionTicketAppData& app_data) {} - - // Different QUIC applications may set some application data in - // the session ticket (e.g. http/3 would set server settings in the - // application data). By default, there's nothing to get. - virtual SessionTicketAppData::Status GetSessionTicketAppData( - const SessionTicketAppData& app_data, - SessionTicketAppData::Flag flag) { - return flag == SessionTicketAppData::Flag::STATUS_RENEW ? - SessionTicketAppData::Status::TICKET_USE_RENEW : - SessionTicketAppData::Status::TICKET_USE; - } - - virtual void StreamHeaders( - int64_t stream_id, - int kind, - const std::vector<std::unique_ptr<QuicHeader>>& headers, - int64_t push_id = 0); - - virtual void StreamClose( - int64_t stream_id, - uint64_t app_error_code); - - virtual void StreamReset( - int64_t stream_id, - uint64_t app_error_code); - - virtual bool SubmitInformation( - int64_t stream_id, - v8::Local<v8::Array> headers) { return false; } - - virtual bool SubmitHeaders( - int64_t stream_id, - v8::Local<v8::Array> headers, - uint32_t flags) { return false; } - - virtual bool SubmitTrailers( - int64_t stream_id, - v8::Local<v8::Array> headers) { return false; } - - virtual BaseObjectPtr<QuicStream> SubmitPush( - int64_t stream_id, - v8::Local<v8::Array> headers) { - // By default, push streams are not supported - // by an application. - return {}; - } - - inline Environment* env() const; - - bool SendPendingData(); - size_t max_header_pairs() const { return max_header_pairs_; } - size_t max_header_length() const { return max_header_length_; } - - protected: - QuicSession* session() const { return session_.get(); } - bool needs_init() const { return needs_init_; } - void set_init_done() { needs_init_ = false; } - inline void set_stream_fin(int64_t stream_id); - void set_max_header_pairs(size_t max) { max_header_pairs_ = max; } - void set_max_header_length(size_t max) { max_header_length_ = max; } - inline std::unique_ptr<QuicPacket> CreateStreamDataPacket(); - - struct StreamData { - size_t count = 0; - size_t remaining = 0; - int64_t id = -1; - int fin = 0; - ngtcp2_vec data[kMaxVectorCount] {}; - ngtcp2_vec* buf = nullptr; - BaseObjectPtr<QuicStream> stream; - StreamData() { buf = data; } - }; - - void Acknowledge( - int64_t stream_id, - uint64_t offset, - size_t datalen); - virtual int GetStreamData(StreamData* data) = 0; - virtual bool StreamCommit(StreamData* data, size_t datalen) = 0; - virtual bool ShouldSetFin(const StreamData& data) = 0; - - ssize_t WriteVStream( - QuicPathStorage* path, - uint8_t* buf, - ssize_t* ndatalen, - const StreamData& stream_data); - - private: - void MaybeSetFin(const StreamData& stream_data); - BaseObjectWeakPtr<QuicSession> session_; - bool needs_init_ = true; - size_t max_header_pairs_ = 0; - size_t max_header_length_ = 0; -}; - -// QUICSESSION_FLAGS are converted into is_{name}() and set_{name}(bool on) -// accessors on the QuicSession class. -#define QUICSESSION_FLAGS(V) \ - V(WRAPPED, wrapped) \ - V(CLOSING, closing) \ - V(GRACEFUL_CLOSING, graceful_closing) \ - V(DESTROYED, destroyed) \ - V(TRANSPORT_PARAMS_SET, transport_params_set) \ - V(NGTCP2_CALLBACK, in_ngtcp2_callback) \ - V(CONNECTION_CLOSE_SCOPE, in_connection_close_scope) \ - V(SILENT_CLOSE, silent_closing) \ - V(STATELESS_RESET, stateless_reset) \ - V(CLOSING_TIMER_ENABLED, closing_timer_enabled) - -// QUIC sessions are logical connections that exchange data -// back and forth between peer endpoints via UDP. Every QuicSession -// has an associated TLS context and all data transferred between -// the peers is always encrypted. Unlike TLS over TCP, however, -// The QuicSession uses a session identifier that is independent -// of both the local *and* peer IP address, allowing a QuicSession -// to persist across changes in the network (one of the key features -// of QUIC). QUIC sessions also support 0RTT, implement error -// correction mechanisms to recover from lost packets, and flow -// control. In other words, there's quite a bit going on within -// a QuicSession object. -class QuicSession final : public AsyncWrap, - public mem::NgLibMemoryManager< - QuicSession, - ngtcp2_mem>, - public StatsBase<QuicSessionStatsTraits> { - public: - // The default preferred address strategy is to ignore it - static void IgnorePreferredAddressStrategy( - QuicSession* session, - const PreferredAddress& preferred_address); - - static void UsePreferredAddressStrategy( - QuicSession* session, - const PreferredAddress& preferred_address); - - static void Initialize( - Environment* env, - v8::Local<v8::Object> target, - v8::Local<v8::Context> context); - - static BaseObjectPtr<QuicSession> CreateServer( - QuicSocket* socket, - const QuicSessionConfig& config, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - const QuicCID& dcid, - const QuicCID& scid, - const QuicCID& ocid, - uint32_t version, - const std::string& alpn = NGHTTP3_ALPN_H3, - uint32_t options = 0, - QlogMode qlog = QlogMode::kDisabled); - - static BaseObjectPtr<QuicSession> CreateClient( - QuicSocket* socket, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - BaseObjectPtr<crypto::SecureContext> secure_context, - ngtcp2_transport_params* early_transport_params, - crypto::SSLSessionPointer early_session_ticket, - v8::Local<v8::Value> dcid, - PreferredAddressStrategy preferred_address_strategy = - IgnorePreferredAddressStrategy, - const std::string& alpn = NGHTTP3_ALPN_H3, - const std::string& hostname = "", - uint32_t options = 0, - QlogMode qlog = QlogMode::kDisabled); - - static const int kInitialClientBufferLength = 4096; - - QuicSession( - ngtcp2_crypto_side side, - // The QuicSocket that created this session. Note that - // it is possible to replace this socket later, after - // the TLS handshake has completed. The QuicSession - // should never assume that the socket will always - // remain the same. - QuicSocket* socket, - v8::Local<v8::Object> wrap, - BaseObjectPtr<crypto::SecureContext> secure_context, - AsyncWrap::ProviderType provider_type, - // QUIC is generally just a transport. The ALPN identifier - // is used to specify the application protocol that is - // layered on top. If not specified, this will default - // to the HTTP/3 identifier. For QUIC, the alpn identifier - // is always required. - const std::string& alpn, - const std::string& hostname, - const QuicCID& dcid, - uint32_t options = 0, - PreferredAddressStrategy preferred_address_strategy = - IgnorePreferredAddressStrategy); - - // Server Constructor - QuicSession( - QuicSocket* socket, - const QuicSessionConfig& config, - v8::Local<v8::Object> wrap, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - const QuicCID& dcid, - const QuicCID& scid, - const QuicCID& ocid, - uint32_t version, - const std::string& alpn, - uint32_t options, - QlogMode qlog); - - // Client Constructor - QuicSession( - QuicSocket* socket, - v8::Local<v8::Object> wrap, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - BaseObjectPtr<crypto::SecureContext> secure_context, - ngtcp2_transport_params* early_transport_params, - crypto::SSLSessionPointer early_session_ticket, - v8::Local<v8::Value> dcid, - PreferredAddressStrategy preferred_address_strategy, - const std::string& alpn, - const std::string& hostname, - uint32_t options, - QlogMode qlog); - - ~QuicSession() override; - - std::string diagnostic_name() const override; - - inline QuicCID dcid() const; - - QuicApplication* application() const { return application_.get(); } - - QuicCryptoContext* crypto_context() const { return crypto_context_.get(); } - - QuicSessionListener* listener() const { return listener_; } - - BaseObjectPtr<QuicStream> CreateStream(int64_t id); - - BaseObjectPtr<QuicStream> FindStream(int64_t id) const; - - inline bool HasStream(int64_t id) const; - - inline bool allow_early_data() const; - -#define V(id, name) \ - bool is_##name() const { return flags_ & (1 << QUICSESSION_FLAG_##id); } \ - void set_##name(bool on = true) { \ - if (on) \ - flags_ |= (1 << QUICSESSION_FLAG_##id); \ - else \ - flags_ &= ~(1 << QUICSESSION_FLAG_##id); \ - } - QUICSESSION_FLAGS(V) -#undef V - - // Returns true if the QuicSession has entered the - // closing period after sending a CONNECTION_CLOSE. - // While true, the QuicSession is only permitted to - // transmit CONNECTION_CLOSE frames until either the - // idle timeout period elapses or until the QuicSession - // is explicitly destroyed. - inline bool is_in_closing_period() const; - - // Returns true if the QuicSession has received a - // CONNECTION_CLOSE frame from the peer. Once in - // the draining period, the QuicSession is not - // permitted to send any frames to the peer. The - // QuicSession will be silently closed after either - // the idle timeout period elapses or until the - // QuicSession is explicitly destroyed. - inline bool is_in_draining_period() const; - - inline bool is_server() const; - - // Starting a GracefulClose disables the ability to open or accept - // new streams for this session. Existing streams are allowed to - // close naturally on their own. Once called, the QuicSession will - // be immediately closed once there are no remaining streams. Note - // that no notification is given to the connecting peer that we're - // in a graceful closing state. A CONNECTION_CLOSE will be sent only - // once Close() is called. - inline void StartGracefulClose(); - - QuicError last_error() const { return last_error_; } - - size_t max_packet_length() const { return max_pktlen_; } - - BaseObjectPtr<QLogStream> qlog_stream(); - - // Get the ALPN protocol identifier configured for this QuicSession. - // For server sessions, this will be compared against the client requested - // ALPN identifier to determine if there is a protocol match. - const std::string& alpn() const { return alpn_; } - - // Get the hostname configured for this QuicSession. This is generally - // only used by client sessions. - const std::string& hostname() const { return hostname_; } - - // Returns the associated peer's address. Note that this - // value can change over the lifetime of the QuicSession. - // The fact that the session is not tied intrinsically to - // a single address is one of the benefits of QUIC. - const SocketAddress& remote_address() const { return remote_address_; } - - inline QuicSocket* socket() const; - - ngtcp2_conn* connection() const { return connection_.get(); } - - void AddStream(BaseObjectPtr<QuicStream> stream); - - void AddToSocket(QuicSocket* socket); - - // Immediately discards the state of the QuicSession - // and renders the QuicSession instance completely - // unusable. - void Destroy(); - - // Extends the QUIC stream flow control window. This is - // called after received data has been consumed and we - // want to allow the peer to send more data. - inline void ExtendStreamOffset(int64_t stream_id, size_t amount); - - // Extends the QUIC session flow control window - inline void ExtendOffset(size_t amount); - - // Retrieve the local transport parameters established for - // this ngtcp2_conn - inline void GetLocalTransportParams(ngtcp2_transport_params* params); - - // The QUIC version that has been negotiated for this session - inline uint32_t negotiated_version() const; - - // True only if ngtcp2 considers the TLS handshake to be completed - inline bool is_handshake_completed() const; - - // Checks to see if data needs to be retransmitted - void OnRetransmitTimeout(); - - // Called when the session has been determined to have been - // idle for too long and needs to be torn down. - inline void OnIdleTimeout(); - - bool OpenBidirectionalStream(int64_t* stream_id); - - bool OpenUnidirectionalStream(int64_t* stream_id); - - // Ping causes the QuicSession to serialize any currently - // pending frames in it's queue, including any necessary - // PROBE packets. This is a best attempt, fire-and-forget - // type of operation. There is no way to listen for a ping - // response. The main intent of using Ping is to either keep - // the connection from becoming idle or to update RTT stats. - void Ping(); - - // Receive and process a QUIC packet received from the peer - bool Receive( - ssize_t nread, - const uint8_t* data, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags); - - // Receive a chunk of QUIC stream data received from the peer - bool ReceiveStreamData( - uint32_t flags, - int64_t stream_id, - const uint8_t* data, - size_t datalen, - uint64_t offset); - - void RemoveStream(int64_t stream_id); - - void RemoveFromSocket(); - - // Causes pending ngtcp2 frames to be serialized and sent - void SendPendingData(); - - inline void ShutdownStream(int64_t stream_id, uint64_t code); - - inline bool SendPacket( - std::unique_ptr<QuicPacket> packet, - const ngtcp2_path_storage& path); - - inline uint64_t max_data_left() const; - - inline uint64_t max_local_streams_uni() const; - - inline void set_last_error( - QuicError error = { - uint32_t{QUIC_ERROR_SESSION}, - uint64_t{NGTCP2_NO_ERROR} - }); - - inline void set_last_error(int32_t family, uint64_t error_code); - - inline void set_last_error(int32_t family, int error_code); - - inline void set_remote_transport_params(); - - bool set_socket(QuicSocket* socket, bool nat_rebinding = false); - - int set_session(SSL_SESSION* session); - - const StreamsMap& streams() const { return streams_; } - - void ResumeStream(int64_t stream_id); - - // Submits informational headers to the QUIC Application - // implementation. If headers are not supported, false - // will be returned. Otherwise, returns true - inline bool SubmitInformation( - int64_t stream_id, - v8::Local<v8::Array> headers); - - // Submits initial headers to the QUIC Application - // implementation. If headers are not supported, false - // will be returned. Otherwise, returns true - inline bool SubmitHeaders( - int64_t stream_id, - v8::Local<v8::Array> headers, - uint32_t flags); - - // Submits trailing headers to the QUIC Application - // implementation. If headers are not supported, false - // will be returned. Otherwise, returns true - inline bool SubmitTrailers( - int64_t stream_id, - v8::Local<v8::Array> headers); - - inline BaseObjectPtr<QuicStream> SubmitPush( - int64_t stream_id, - v8::Local<v8::Array> headers); - - // Error handling for the QuicSession. client and server - // instances will do different things here, but ultimately - // an error means that the QuicSession - // should be torn down. - void HandleError(); - - bool SendConnectionClose(); - - bool IsResetToken( - const QuicCID& cid, - const uint8_t* data, - size_t datalen); - - // Implementation for mem::NgLibMemoryManager - inline void CheckAllocatedSize(size_t previous_size) const; - - inline void IncreaseAllocatedSize(size_t size); - - inline void DecreaseAllocatedSize(size_t size); - - // Initiate closing of the QuicSession. This will round trip - // through JavaScript, causing all currently opened streams - // to be closed. If the SESSION_CLOSE_FLAG_SILENT flag is - // set, the connected peer will not be notified, otherwise - // an attempt will be made to send a CONNECTION_CLOSE frame - // to the peer. If Close is called while within the ngtcp2 - // callback scope, sending the CONNECTION_CLOSE will be - // deferred until the ngtcp2 callback scope exits. - void Close( - int close_flags = QuicSessionListener::SESSION_CLOSE_FLAG_NONE); - - void PushListener(QuicSessionListener* listener); - - void RemoveListener(QuicSessionListener* listener); - - inline bool is_unable_to_send_packets(); - - inline void set_connection_id_strategy( - ConnectionIDStrategy strategy); - - inline void set_preferred_address_strategy( - PreferredAddressStrategy strategy); - - inline void SetSessionTicketAppData( - const SessionTicketAppData& app_data); - - inline SessionTicketAppData::Status GetSessionTicketAppData( - const SessionTicketAppData& app_data, - SessionTicketAppData::Flag flag); - - inline void SelectPreferredAddress( - const PreferredAddress& preferred_address); - - // Report that the stream data is flow control blocked - inline void StreamDataBlocked(int64_t stream_id); - - // SendSessionScope triggers SendPendingData() when not executing - // within the context of an ngtcp2 callback. When within an ngtcp2 - // callback, SendPendingData will always be called when the callbacks - // complete. - class SendSessionScope final { - public: - explicit SendSessionScope(QuicSession* session) - : session_(session) { - CHECK(session_); - } - - SendSessionScope(const SendSessionScope& other) = delete; - - ~SendSessionScope() { - if (NgCallbackScope::InNgCallbackScope(session_.get()) || - session_->is_in_closing_period() || - session_->is_in_draining_period()) { - return; - } - session_->SendPendingData(); - } - - private: - BaseObjectPtr<QuicSession> session_; - }; - - // ConnectionCloseScope triggers sending a CONNECTION_CLOSE - // when not executing within the context of an ngtcp2 callback - // and the session is in the correct state. - class ConnectionCloseScope final { - public: - ConnectionCloseScope(QuicSession* session, bool silent = false) - : session_(session), - silent_(silent) { - CHECK(session_); - // If we are already in a ConnectionCloseScope, ignore. - if (session_->is_in_connection_close_scope()) - silent_ = true; - else - session_->set_in_connection_close_scope(); - } - - ConnectionCloseScope(const ConnectionCloseScope& other) = delete; - - ~ConnectionCloseScope() { - if (silent_ || - NgCallbackScope::InNgCallbackScope(session_.get()) || - session_->is_in_closing_period() || - session_->is_in_draining_period()) { - return; - } - session_->set_in_connection_close_scope(false); - session_->SendConnectionClose(); - } - - private: - BaseObjectPtr<QuicSession> session_; - bool silent_ = false; - }; - - // Tracks whether or not we are currently within an ngtcp2 callback - // function. Certain ngtcp2 APIs are not supposed to be called when - // within a callback. We use this as a gate to check. - class NgCallbackScope final { - public: - explicit NgCallbackScope(QuicSession* session) : session_(session) { - CHECK(session_); - CHECK(!InNgCallbackScope(session)); - session_->set_in_ngtcp2_callback(); - } - - NgCallbackScope(const NgCallbackScope& other) = delete; - - ~NgCallbackScope() { - session_->set_in_ngtcp2_callback(false); - } - - static bool InNgCallbackScope(QuicSession* session) { - return session->is_in_ngtcp2_callback(); - } - - private: - BaseObjectPtr<QuicSession> session_; - }; - - QuicState* quic_state() { return quic_state_.get(); } - - void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(QuicSession) - SET_SELF_SIZE(QuicSession) - - private: - static void RandomConnectionIDStrategy( - QuicSession* session, - ngtcp2_cid* cid, - size_t cidlen); - - // Initialize the QuicSession as a server - void InitServer( - QuicSessionConfig config, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - const QuicCID& dcid, - const QuicCID& scid, - const QuicCID& ocid, - uint32_t version, - QlogMode qlog); - - // Initialize the QuicSession as a client - void InitClient( - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - ngtcp2_transport_params* early_transport_params, - crypto::SSLSessionPointer early_session_ticket, - v8::Local<v8::Value> dcid, - QlogMode qlog); - - bool InitApplication(); - - void AckedStreamDataOffset( - int64_t stream_id, - uint64_t offset, - uint64_t datalen); - - void ExtendMaxStreamData(int64_t stream_id, uint64_t max_data); - - void ExtendMaxStreams(bool bidi, uint64_t max_streams); - - void ExtendMaxStreamsUni(uint64_t max_streams); - - void ExtendMaxStreamsBidi(uint64_t max_streams); - - void ExtendMaxStreamsRemoteUni(uint64_t max_streams); - - void ExtendMaxStreamsRemoteBidi(uint64_t max_streams); - - void GetNewConnectionID(ngtcp2_cid* cid, uint8_t* token, size_t cidlen); - - void GetConnectionCloseInfo(); - - void HandshakeCompleted(); - - void HandshakeConfirmed(); - - void PathValidation( - const ngtcp2_path* path, - ngtcp2_path_validation_result res); - - bool ReceivePacket(ngtcp2_path* path, const uint8_t* data, ssize_t nread); - - void ScheduleRetransmit(); - - bool SendPacket(std::unique_ptr<QuicPacket> packet); - - void StreamClose(int64_t stream_id, uint64_t app_error_code); - - void StreamReset( - int64_t stream_id, - uint64_t final_size, - uint64_t app_error_code); - - bool WritePackets(const char* diagnostic_label = nullptr); - - void UpdateConnectionID( - int type, - const QuicCID& cid, - const StatelessResetToken& token); - - void UpdateDataStats(); - - void UpdateEndpoint(const ngtcp2_path& path); - - void VersionNegotiation(const uint32_t* sv, size_t nsv); - - void UpdateIdleTimer(); - - void UpdateClosingTimer(); - - void UpdateRetransmitTimer(uint64_t timeout); - - bool StartClosingPeriod(); - - void IncrementConnectionCloseAttempts(); - - bool ShouldAttemptConnectionClose(); - - static int OnReceiveCryptoData( - ngtcp2_conn* conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, - const uint8_t* data, - size_t datalen, - void* user_data); - - static int OnHandshakeCompleted( - ngtcp2_conn* conn, - void* user_data); - - static int OnHandshakeConfirmed( - ngtcp2_conn* conn, - void* user_data); - - static int OnReceiveStreamData( - ngtcp2_conn* conn, - uint32_t flags, - int64_t stream_id, - uint64_t offset, - const uint8_t* data, - size_t datalen, - void* user_data, - void* stream_user_data); - - static int OnAckedCryptoOffset( - ngtcp2_conn* conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, - uint64_t datalen, - void* user_data); - - static int OnAckedStreamDataOffset( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t offset, - uint64_t datalen, - void* user_data, - void* stream_user_data); - - static int OnSelectPreferredAddress( - ngtcp2_conn* conn, - ngtcp2_addr* dest, - const ngtcp2_preferred_addr* paddr, - void* user_data); - - static int OnStreamClose( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t app_error_code, - void* user_data, - void* stream_user_data); - - static int OnStreamOpen( - ngtcp2_conn* conn, - int64_t stream_id, - void* user_data); - - static int OnStreamReset( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t final_size, - uint64_t app_error_code, - void* user_data, - void* stream_user_data); - - static int OnRand( - uint8_t* dest, - size_t destlen, - ngtcp2_rand_ctx ctx); - - static int OnGetNewConnectionID( - ngtcp2_conn* conn, - ngtcp2_cid* cid, - uint8_t* token, - size_t cidlen, - void* user_data); - - static int OnRemoveConnectionID( - ngtcp2_conn* conn, - const ngtcp2_cid* cid, - void* user_data); - - static int OnPathValidation( - ngtcp2_conn* conn, - const ngtcp2_path* path, - ngtcp2_path_validation_result res, - void* user_data); - - static int OnExtendMaxStreamsUni( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data); - - static int OnExtendMaxStreamsBidi( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data); - - static int OnExtendMaxStreamData( - ngtcp2_conn* conn, - int64_t stream_id, - uint64_t max_data, - void* user_data, - void* stream_user_data); - - static int OnVersionNegotiation( - ngtcp2_conn* conn, - const ngtcp2_pkt_hd* hd, - const uint32_t* sv, - size_t nsv, - void* user_data); - - static int OnStatelessReset( - ngtcp2_conn* conn, - const ngtcp2_pkt_stateless_reset* sr, - void* user_data); - - static int OnExtendMaxStreamsRemoteUni( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data); - - static int OnExtendMaxStreamsRemoteBidi( - ngtcp2_conn* conn, - uint64_t max_streams, - void* user_data); - - static int OnConnectionIDStatus( - ngtcp2_conn* conn, - int type, - uint64_t seq, - const ngtcp2_cid* cid, - const uint8_t* token, - void* user_data); - - static void OnQlogWrite( - void* user_data, - uint32_t flags, - const void* data, - size_t len); - -#define V(id, _) QUICSESSION_FLAG_##id, - enum QuicSessionFlags : uint32_t { - QUICSESSION_FLAGS(V) - QUICSESSION_FLAG_COUNT - }; -#undef V - - // Select the QUIC Application based on the configured ALPN identifier - QuicApplication* SelectApplication(QuicSession* session); - - ngtcp2_mem alloc_info_; - std::unique_ptr<QuicCryptoContext> crypto_context_; - std::unique_ptr<QuicApplication> application_; - BaseObjectWeakPtr<QuicSocket> socket_; - std::string alpn_; - std::string hostname_; - QuicError last_error_ = { - uint32_t{QUIC_ERROR_SESSION}, - uint64_t{NGTCP2_NO_ERROR} - }; - ConnectionPointer connection_; - SocketAddress local_address_{}; - SocketAddress remote_address_{}; - uint32_t flags_ = 0; - size_t max_pktlen_ = 0; - size_t current_ngtcp2_memory_ = 0; - size_t connection_close_attempts_ = 0; - size_t connection_close_limit_ = 1; - - ConnectionIDStrategy connection_id_strategy_ = nullptr; - PreferredAddressStrategy preferred_address_strategy_ = nullptr; - - QuicSessionListener* listener_ = nullptr; - JSQuicSessionListener default_listener_; - - TimerWrapHandle idle_; - TimerWrapHandle retransmit_; - - QuicCID scid_; - QuicCID dcid_; - QuicCID pscid_; - ngtcp2_transport_params transport_params_; - - std::unique_ptr<QuicPacket> conn_closebuf_; - - StreamsMap streams_; - - AliasedStruct<QuicSessionState> state_; - - struct RemoteTransportParamsDebug { - QuicSession* session; - explicit RemoteTransportParamsDebug(QuicSession* session_) - : session(session_) {} - std::string ToString() const; - }; - - static const ngtcp2_conn_callbacks callbacks[2]; - - BaseObjectPtr<QuicState> quic_state_; - BaseObjectWeakPtr<QLogStream> qlog_stream_; - - friend class QuicCryptoContext; - friend class QuicSessionListener; - friend class JSQuicSessionListener; -}; - -class QuicCallbackScope { - public: - explicit QuicCallbackScope(QuicSession* session); - ~QuicCallbackScope(); - - void operator=(const QuicCallbackScope&) = delete; - void operator=(QuicCallbackScope&&) = delete; - QuicCallbackScope(const QuicCallbackScope&) = delete; - QuicCallbackScope(QuicCallbackScope&&) = delete; - - private: - BaseObjectPtr<QuicSession> session_; - std::unique_ptr<InternalCallbackScope> private_; - v8::TryCatch try_catch_; -}; - -} // namespace quic -} // namespace node - -#endif // NODE_WANT_INTERNALS -#endif // SRC_QUIC_NODE_QUIC_SESSION_H_ diff --git a/src/quic/node_quic_socket-inl.h b/src/quic/node_quic_socket-inl.h deleted file mode 100644 index 8e7bc65d784..00000000000 --- a/src/quic/node_quic_socket-inl.h +++ /dev/null @@ -1,192 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_SOCKET_INL_H_ -#define SRC_QUIC_NODE_QUIC_SOCKET_INL_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "node_quic_socket.h" -#include "node_sockaddr-inl.h" -#include "node_quic_session.h" -#include "node_crypto.h" -#include "debug_utils-inl.h" - -namespace node { - -using crypto::EntropySource; - -namespace quic { - -std::unique_ptr<QuicPacket> QuicPacket::Create( - const char* diagnostic_label, - size_t len) { - CHECK_LE(len, MAX_PKTLEN); - return std::make_unique<QuicPacket>(diagnostic_label, len); -} - -std::unique_ptr<QuicPacket> QuicPacket::Copy( - const std::unique_ptr<QuicPacket>& other) { - return std::make_unique<QuicPacket>(*other.get()); -} - -void QuicPacket::set_length(size_t len) { - CHECK_LE(len, MAX_PKTLEN); - len_ = len; -} - -int QuicEndpoint::Send( - uv_buf_t* buf, - size_t len, - const sockaddr* addr) { - int ret = static_cast<int>(udp_->Send(buf, len, addr)); - if (ret == 0) - IncrementPendingCallbacks(); - return ret; -} - -int QuicEndpoint::ReceiveStart() { - return udp_->RecvStart(); -} - -int QuicEndpoint::ReceiveStop() { - return udp_->RecvStop(); -} - -void QuicEndpoint::WaitForPendingCallbacks() { - if (!has_pending_callbacks()) { - listener_->OnEndpointDone(this); - return; - } - waiting_for_callbacks_ = true; -} - -void QuicSocket::AssociateCID( - const QuicCID& cid, - const QuicCID& scid) { - if (cid && scid) - dcid_to_scid_[cid] = scid; -} - -void QuicSocket::DisassociateCID(const QuicCID& cid) { - if (cid) { - Debug(this, "Removing association for cid %s", cid); - dcid_to_scid_.erase(cid); - } -} - -void QuicSocket::AssociateStatelessResetToken( - const StatelessResetToken& token, - BaseObjectPtr<QuicSession> session) { - Debug(this, "Associating stateless reset token %s", token); - token_map_[token] = session; -} - -SocketAddress QuicSocket::local_address() const { - DCHECK(preferred_endpoint_); - return preferred_endpoint_->local_address(); -} - -void QuicSocket::DisassociateStatelessResetToken( - const StatelessResetToken& token) { - Debug(this, "Removing stateless reset token %s", token); - token_map_.erase(token); -} - -void QuicSocket::ReceiveStart() { - for (const auto& endpoint : endpoints_) - CHECK_EQ(endpoint->ReceiveStart(), 0); -} - -void QuicSocket::ReceiveStop() { - for (const auto& endpoint : endpoints_) - CHECK_EQ(endpoint->ReceiveStop(), 0); -} - -void QuicSocket::RemoveSession( - const QuicCID& cid, - const SocketAddress& addr) { - DecrementSocketAddressCounter(addr); - sessions_.erase(cid); -} - -void QuicSocket::ReportSendError(int error) { - listener_->OnError(error); -} - -void QuicSocket::IncrementStatelessResetCounter(const SocketAddress& addr) { - addrLRU_.Upsert(addr)->reset_count++; -} - -void QuicSocket::IncrementSocketAddressCounter(const SocketAddress& addr) { - addrLRU_.Upsert(addr)->active_connections++; -} - -void QuicSocket::DecrementSocketAddressCounter(const SocketAddress& addr) { - SocketAddressInfo* counts = addrLRU_.Peek(addr); - if (counts != nullptr && counts->active_connections > 0) - counts->active_connections--; -} - -size_t QuicSocket::GetCurrentSocketAddressCounter(const SocketAddress& addr) { - SocketAddressInfo* counts = addrLRU_.Peek(addr); - return counts != nullptr ? counts->active_connections : 0; -} - -size_t QuicSocket::GetCurrentStatelessResetCounter(const SocketAddress& addr) { - SocketAddressInfo* counts = addrLRU_.Peek(addr); - return counts != nullptr ? counts->reset_count : 0; -} - -void QuicSocket::ServerBusy(bool on) { - Debug(this, "Turning Server Busy Response %s", on ? "on" : "off"); - state_->server_busy = on ? 1 : 0; - listener_->OnServerBusy(); -} - -bool QuicSocket::is_diagnostic_packet_loss(double prob) const { - if (LIKELY(prob == 0.0)) return false; - unsigned char c = 255; - EntropySource(&c, 1); - return (static_cast<double>(c) / 255) < prob; -} - -void QuicSocket::set_diagnostic_packet_loss(double rx, double tx) { - rx_loss_ = rx; - tx_loss_ = tx; -} - -void QuicSocket::set_validated_address(const SocketAddress& addr) { - addrLRU_.Upsert(addr)->validated = true; -} - -bool QuicSocket::is_validated_address(const SocketAddress& addr) const { - auto info = addrLRU_.Peek(addr); - return info != nullptr ? info->validated : false; -} - -void QuicSocket::AddSession( - const QuicCID& cid, - BaseObjectPtr<QuicSession> session) { - sessions_[cid] = session; - IncrementSocketAddressCounter(session->remote_address()); - IncrementStat( - session->is_server() ? - &QuicSocketStats::server_sessions : - &QuicSocketStats::client_sessions); -} - -void QuicSocket::AddEndpoint( - BaseObjectPtr<QuicEndpoint> endpoint_, - bool preferred) { - Debug(this, "Adding %sendpoint", preferred ? "preferred " : ""); - if (preferred || endpoints_.empty()) - preferred_endpoint_ = endpoint_; - endpoints_.emplace_back(endpoint_); - if (state_->server_listening) - endpoint_->ReceiveStart(); -} - -} // namespace quic -} // namespace node - -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_SOCKET_INL_H_ diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc deleted file mode 100644 index 810705014ca..00000000000 --- a/src/quic/node_quic_socket.cc +++ /dev/null @@ -1,1202 +0,0 @@ -#include "node_quic_socket-inl.h" // NOLINT(build/include) -#include "aliased_struct-inl.h" -#include "allocated_buffer-inl.h" -#include "async_wrap-inl.h" -#include "debug_utils-inl.h" -#include "env-inl.h" -#include "memory_tracker-inl.h" -#include "nghttp2/nghttp2.h" -#include "nghttp3/nghttp3.h" -#include "node.h" -#include "node_buffer.h" -#include "node_crypto.h" -#include "node_internals.h" -#include "node_mem-inl.h" -#include "node_quic_crypto.h" -#include "node_quic_session-inl.h" -#include "node_quic_util-inl.h" -#include "node_sockaddr-inl.h" -#include "req_wrap-inl.h" -#include "util.h" -#include "uv.h" -#include "v8.h" - -#include <random> - -namespace node { - -using crypto::EntropySource; -using crypto::SecureContext; - -using v8::ArrayBufferView; -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::HandleScope; -using v8::Isolate; -using v8::Local; -using v8::Number; -using v8::Object; -using v8::ObjectTemplate; -using v8::PropertyAttribute; -using v8::Value; - -namespace quic { - -namespace { -// The reserved version is a mechanism QUIC endpoints -// can use to ensure correct handling of version -// negotiation. It is defined by the QUIC spec in -// https://tools.ietf.org/html/draft-ietf-quic-transport-24#section-6.3 -// Specifically, any version that follows the pattern -// 0x?a?a?a?a may be used to force version negotiation. -inline uint32_t GenerateReservedVersion( - const SocketAddress& addr, - uint32_t version) { - socklen_t addrlen = addr.length(); - uint32_t h = 0x811C9DC5u; - const uint8_t* p = addr.raw(); - const uint8_t* ep = p + addrlen; - for (; p != ep; ++p) { - h ^= *p; - h *= 0x01000193u; - } - version = htonl(version); - p = reinterpret_cast<const uint8_t*>(&version); - ep = p + sizeof(version); - for (; p != ep; ++p) { - h ^= *p; - h *= 0x01000193u; - } - h &= 0xf0f0f0f0u; - h |= 0x0a0a0a0au; - return h; -} - -bool IsShortHeader( - uint32_t version, - const uint8_t* pscid, - size_t pscidlen) { - return version == NGTCP2_PROTO_VER && - pscid == nullptr && - pscidlen == 0; -} -} // namespace - -QuicPacket::QuicPacket(const char* diagnostic_label, size_t len) - : data_{0}, - len_(len), - diagnostic_label_(diagnostic_label) { - CHECK_LE(len, MAX_PKTLEN); -} - -QuicPacket::QuicPacket(const QuicPacket& other) : - QuicPacket(other.diagnostic_label_, other.len_) { - memcpy(&data_, &other.data_, other.len_); -} - -const char* QuicPacket::diagnostic_label() const { - return diagnostic_label_ != nullptr ? - diagnostic_label_ : "unspecified"; -} - -QuicSocketListener::~QuicSocketListener() { - if (socket_) - socket_->RemoveListener(this); -} - -void QuicSocketListener::OnError(ssize_t code) { - if (previous_listener_ != nullptr) - previous_listener_->OnError(code); -} - -void QuicSocketListener::OnSessionReady(BaseObjectPtr<QuicSession> session) { - if (previous_listener_ != nullptr) - previous_listener_->OnSessionReady(session); -} - -void QuicSocketListener::OnServerBusy() { - if (previous_listener_ != nullptr) - previous_listener_->OnServerBusy(); -} - -void QuicSocketListener::OnEndpointDone(QuicEndpoint* endpoint) { - if (previous_listener_ != nullptr) - previous_listener_->OnEndpointDone(endpoint); -} - -void QuicSocketListener::OnDestroy() { - if (previous_listener_ != nullptr) - previous_listener_->OnDestroy(); -} - -void JSQuicSocketListener::OnError(ssize_t code) { - Environment* env = socket()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - Local<Value> arg = Number::New(env->isolate(), static_cast<double>(code)); - socket()->MakeCallback(env->quic_on_socket_close_function(), 1, &arg); -} - -void JSQuicSocketListener::OnSessionReady(BaseObjectPtr<QuicSession> session) { - Environment* env = socket()->env(); - Local<Value> arg = session->object(); - Context::Scope context_scope(env->context()); - socket()->MakeCallback(env->quic_on_session_ready_function(), 1, &arg); -} - -void JSQuicSocketListener::OnServerBusy() { - Environment* env = socket()->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - socket()->MakeCallback( - env->quic_on_socket_server_busy_function(), 0, nullptr); -} - -void JSQuicSocketListener::OnEndpointDone(QuicEndpoint* endpoint) { - Environment* env = socket()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - MakeCallback( - env->isolate(), - endpoint->object(), - env->ondone_string(), - 0, nullptr); -} - -void JSQuicSocketListener::OnDestroy() { - // Do nothing here. -} - -QuicEndpoint::QuicEndpoint( - QuicState* quic_state, - Local<Object> wrap, - QuicSocket* listener, - Local<Object> udp_wrap) - : BaseObject(quic_state->env(), wrap), - listener_(listener), - quic_state_(quic_state) { - MakeWeak(); - udp_ = static_cast<UDPWrapBase*>( - udp_wrap->GetAlignedPointerFromInternalField( - UDPWrapBase::kUDPWrapBaseField)); - CHECK_NOT_NULL(udp_); - udp_->set_listener(this); - strong_ptr_.reset(udp_->GetAsyncWrap()); -} - -QuicEndpoint::~QuicEndpoint() { - udp_->set_listener(nullptr); -} - -uv_buf_t QuicEndpoint::OnAlloc(size_t suggested_size) { - return AllocatedBuffer::AllocateManaged(env(), suggested_size).release(); -} - -void QuicEndpoint::OnRecv( - ssize_t nread, - const uv_buf_t& buf_, - const sockaddr* addr, - unsigned int flags) { - AllocatedBuffer buf(env(), buf_); - - if (nread <= 0) { - if (nread < 0) - listener_->OnError(this, nread); - return; - } - - listener_->OnReceive( - nread, - std::move(buf), - local_address(), - SocketAddress(addr), - flags); -} - -ReqWrap<uv_udp_send_t>* QuicEndpoint::CreateSendWrap(size_t msg_size) { - return listener_->OnCreateSendWrap(msg_size); -} - -void QuicEndpoint::OnSendDone(ReqWrap<uv_udp_send_t>* wrap, int status) { - DecrementPendingCallbacks(); - listener_->OnSendDone(wrap, status); - if (!has_pending_callbacks() && waiting_for_callbacks_) - listener_->OnEndpointDone(this); -} - -void QuicEndpoint::OnAfterBind() { - listener_->OnBind(this); -} - -template <typename Fn> -void QuicSocketStatsTraits::ToString(const QuicSocket& ptr, Fn&& add_field) { -#define V(_n, name, label) \ - add_field(label, ptr.GetStat(&QuicSocketStats::name)); - SOCKET_STATS(V) -#undef V -} - -QuicSocket::QuicSocket( - QuicState* quic_state, - Local<Object> wrap, - uint64_t retry_token_expiration, - size_t max_connections, - size_t max_connections_per_host, - size_t max_stateless_resets_per_host, - uint32_t options, - QlogMode qlog, - const uint8_t* session_reset_secret, - bool disable_stateless_reset) - : AsyncWrap(quic_state->env(), wrap, AsyncWrap::PROVIDER_QUICSOCKET), - StatsBase(quic_state->env(), wrap), - alloc_info_(MakeAllocator()), - block_list_(SocketAddressBlockListWrap::New(quic_state->env())), - options_(options), - state_(quic_state->env()->isolate()), - max_connections_(max_connections), - max_connections_per_host_(max_connections_per_host), - max_stateless_resets_per_host_(max_stateless_resets_per_host), - retry_token_expiration_(retry_token_expiration), - qlog_(qlog), - server_alpn_(NGHTTP3_ALPN_H3), - addrLRU_(DEFAULT_MAX_SOCKETADDRESS_LRU_SIZE), - quic_state_(quic_state) { - MakeWeak(); - PushListener(&default_listener_); - - Debug(this, "New QuicSocket created"); - - EntropySource(token_secret_, kTokenSecretLen); - - wrap->DefineOwnProperty( - env()->context(), - env()->block_list_string(), - block_list_->object(), - PropertyAttribute::ReadOnly).Check(); - - wrap->DefineOwnProperty( - env()->context(), - env()->state_string(), - state_.GetArrayBuffer(), - PropertyAttribute::ReadOnly).Check(); - - if (disable_stateless_reset) - state_->stateless_reset_disabled = 1; - - // Set the session reset secret to the one provided or random. - // Note that a random secret is going to make it exceedingly - // difficult for the session reset token to be useful. - if (session_reset_secret != nullptr) { - memcpy(reset_token_secret_, - session_reset_secret, - NGTCP2_STATELESS_RESET_TOKENLEN); - } else { - EntropySource(reset_token_secret_, NGTCP2_STATELESS_RESET_TOKENLEN); - } -} - -QuicSocket::~QuicSocket() { - QuicSocketListener* listener = listener_; - listener_->OnDestroy(); - if (listener == listener_) - RemoveListener(listener_); - - // In a clean shutdown, all QuicSessions associated with the QuicSocket - // would have been destroyed explicitly. However, if the QuicSocket is - // garbage collected / freed before Destroy having been called, there - // may be sessions remaining. This is not really a good thing. - Debug(this, "Destroying with %d sessions remaining", sessions_.size()); - - DebugStats(); -} - -void QuicSocket::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("endpoints", endpoints_); - tracker->TrackField("sessions", sessions_); - tracker->TrackField("dcid_to_scid", dcid_to_scid_); - tracker->TrackField("address_counts", addrLRU_); - tracker->TrackField("token_map", token_map_); - StatsBase::StatsMemoryInfo(tracker); - tracker->TrackFieldWithSize( - "current_ngtcp2_memory", - current_ngtcp2_memory_); -} - -void QuicSocket::Listen( - BaseObjectPtr<SecureContext> sc, - const sockaddr* preferred_address, - const std::string& alpn, - uint32_t options) { - CHECK(sc); - CHECK_NE(state_->server_listening, 1); - Debug(this, "Starting to listen"); - server_session_config_.Set(quic_state(), preferred_address); - server_secure_context_ = sc; - server_alpn_ = alpn; - server_options_ = options; - state_->server_listening = 1; - RecordTimestamp(&QuicSocketStats::listen_at); - ReceiveStart(); -} - -void QuicSocket::OnError(QuicEndpoint* endpoint, ssize_t error) { - // TODO(@jasnell): What should we do with the endpoint? - Debug(this, "Reading data from UDP socket failed. Error %" PRId64, error); - listener_->OnError(error); -} - -ReqWrap<uv_udp_send_t>* QuicSocket::OnCreateSendWrap(size_t msg_size) { - HandleScope handle_scope(env()->isolate()); - Local<Object> obj; - if (!env()->quicsocketsendwrap_instance_template() - ->NewInstance(env()->context()).ToLocal(&obj)) return nullptr; - return last_created_send_wrap_ = new SendWrap(quic_state(), obj, msg_size); -} - -void QuicSocket::OnEndpointDone(QuicEndpoint* endpoint) { - Debug(this, "Endpoint has no pending callbacks"); - listener_->OnEndpointDone(endpoint); -} - -void QuicSocket::OnBind(QuicEndpoint* endpoint) { - SocketAddress local_address = endpoint->local_address(); - bound_endpoints_[local_address] = - BaseObjectWeakPtr<QuicEndpoint>(endpoint); - Debug(this, "Endpoint %s bound", local_address); - RecordTimestamp(&QuicSocketStats::bound_at); -} - -BaseObjectPtr<QuicSession> QuicSocket::FindSession(const QuicCID& cid) { - BaseObjectPtr<QuicSession> session; - auto session_it = sessions_.find(cid); - if (session_it == std::end(sessions_)) { - auto scid_it = dcid_to_scid_.find(cid); - if (scid_it != std::end(dcid_to_scid_)) { - session_it = sessions_.find(scid_it->second); - CHECK_NE(session_it, std::end(sessions_)); - session = session_it->second; - } - } else { - session = session_it->second; - } - return session; -} - -// When a received packet contains a QUIC short header but cannot be -// matched to a known QuicSession, it is either (a) garbage, -// (b) a valid packet for a connection we no longer have state -// for, or (c) a stateless reset. Because we do not yet know if -// we are going to process the packet, we need to try to quickly -// determine -- with as little cost as possible -- whether the -// packet contains a reset token. We do so by checking the final -// NGTCP2_STATELESS_RESET_TOKENLEN bytes in the packet to see if -// they match one of the known reset tokens previously given by -// the remote peer. If there's a match, then it's a reset token, -// if not, we move on the to the next check. It is very important -// that this check be as inexpensive as possible to avoid a DOS -// vector. -bool QuicSocket::MaybeStatelessReset( - const QuicCID& dcid, - const QuicCID& scid, - ssize_t nread, - const uint8_t* data, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags) { - if (UNLIKELY(state_->stateless_reset_disabled || nread < 16)) - return false; - StatelessResetToken possible_token( - data + nread - NGTCP2_STATELESS_RESET_TOKENLEN); - Debug(this, "Possible stateless reset token: %s", possible_token); - auto it = token_map_.find(possible_token); - if (it == token_map_.end()) - return false; - Debug(this, "Received a stateless reset token %s", possible_token); - return it->second->Receive(nread, data, local_addr, remote_addr, flags); -} - -// When a packet is received here, we do not yet know if we can -// process it successfully as a QUIC packet or not. Given the -// nature of UDP, we may receive a great deal of garbage here -// so it is extremely important not to commit resources until -// we're certain we can process the data we received as QUIC -// packet. -// Any packet we choose not to process must be ignored. -void QuicSocket::OnReceive( - ssize_t nread, - AllocatedBuffer buf, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags) { - Debug(this, "Receiving %d bytes from the UDP socket", nread); - - // When diagnostic packet loss is enabled, the packet will be randomly - // dropped based on the rx_loss_ probability. - if (UNLIKELY(is_diagnostic_packet_loss(rx_loss_))) { - Debug(this, "Simulating received packet loss"); - return; - } - - if (UNLIKELY(block_list_->Apply(remote_addr))) { - Debug(this, "Ignoring blocked remote address: %s", remote_addr); - IncrementStat(&QuicSocketStats::packets_ignored); - return; - } - - IncrementStat(&QuicSocketStats::bytes_received, nread); - - const uint8_t* data = reinterpret_cast<const uint8_t*>(buf.data()); - - uint32_t pversion; - const uint8_t* pdcid; - size_t pdcidlen; - const uint8_t* pscid; - size_t pscidlen; - - // This is our first check to see if the received data can be - // processed as a QUIC packet. If this fails, then the QUIC packet - // header is invalid and cannot be processed; all we can do is ignore - // it. It's questionable whether we should even increment the - // packets_ignored statistic here but for now we do. If it succeeds, - // we have a valid QUIC header but there's still no guarantee that - // the packet can be successfully processed. - if (ngtcp2_pkt_decode_version_cid( - &pversion, - &pdcid, - &pdcidlen, - &pscid, - &pscidlen, - data, nread, kScidLen) < 0) { - IncrementStat(&QuicSocketStats::packets_ignored); - return; - } - - // QUIC currently requires CID lengths of max NGTCP2_MAX_CIDLEN. The - // ngtcp2 API allows non-standard lengths, and we may want to allow - // non-standard lengths later. But for now, we're going to ignore any - // packet with a non-standard CID length. - if (pdcidlen > NGTCP2_MAX_CIDLEN || pscidlen > NGTCP2_MAX_CIDLEN) { - IncrementStat(&QuicSocketStats::packets_ignored); - return; - } - - QuicCID dcid(pdcid, pdcidlen); - QuicCID scid(pscid, pscidlen); - - Debug(this, "Received a QUIC packet for dcid %s", dcid); - - BaseObjectPtr<QuicSession> session = FindSession(dcid); - - // If a session is not found, there are four possible reasons: - // 1. The session has not been created yet - // 2. The session existed once but we've lost the local state for it - // 3. The packet is a stateless reset sent by the peer - // 4. This is a malicious or malformed packet. - if (!session) { - Debug(this, "There is no existing session for dcid %s", dcid); - bool is_short_header = IsShortHeader(pversion, pscid, pscidlen); - - // Handle possible reception of a stateless reset token... - // If it is a stateless reset, the packet will be handled with - // no additional action necessary here. We want to return immediately - // without committing any further resources. - if (is_short_header && - MaybeStatelessReset( - dcid, - scid, - nread, - data, - local_addr, - remote_addr, - flags)) { - Debug(this, "Handled stateless reset"); - return; - } - - // AcceptInitialPacket will first validate that the packet can be - // accepted, then create a new server QuicSession instance if able - // to do so. If a new instance cannot be created (for any reason), - // the session BaseObjectPtr will be empty on return. - session = AcceptInitialPacket( - pversion, - dcid, - scid, - nread, - data, - local_addr, - remote_addr, - flags); - - // There are many reasons why a server QuicSession could not be - // created. The most common will be invalid packets or incorrect - // QUIC version. In any of these cases, however, to prevent a - // potential attacker from causing us to consume resources, - // we're just going to ignore the packet. It is possible that - // the AcceptInitialPacket sent a version negotiation packet, - // or a CONNECTION_CLOSE packet. - if (!session) { - Debug(this, "Unable to create a new server QuicSession"); - // If the packet contained a short header, we might need to send - // a stateless reset. The stateless reset contains a token derived - // from the received destination connection ID. - // - // TODO(@jasnell): Stateless resets are generated programmatically - // using HKDF with the sender provided dcid and a locally provided - // secret as input. It is entirely possible that a malicious - // peer could send multiple stateless reset eliciting packets - // with the specific intent of using the returned stateless - // reset to guess the stateless reset token secret used by - // the server. Once guessed, the malicious peer could use - // that secret as a DOS vector against other peers. We currently - // implement some mitigations for this by limiting the number - // of stateless resets that can be sent to a specific remote - // address but there are other possible mitigations, such as - // including the remote address as input in the generation of - // the stateless token. - if (is_short_header && - SendStatelessReset(dcid, local_addr, remote_addr, nread)) { - Debug(this, "Sent stateless reset"); - IncrementStat(&QuicSocketStats::stateless_reset_count); - return; - } - IncrementStat(&QuicSocketStats::packets_ignored); - return; - } - } - - CHECK(session); - // If the QuicSession is already destroyed, there's nothing to do. - if (session->is_destroyed()) - return IncrementStat(&QuicSocketStats::packets_ignored); - - // If the packet could not successfully processed for any reason (possibly - // due to being malformed or malicious in some way) we mark it ignored. - if (!session->Receive(nread, data, local_addr, remote_addr, flags)) { - IncrementStat(&QuicSocketStats::packets_ignored); - return; - } - - IncrementStat(&QuicSocketStats::packets_received); -} - -// Generates and sends a version negotiation packet. This is -// terminal for the connection and is sent only when a QUIC -// packet is received for an unsupported Node.js version. -// It is possible that a malicious packet triggered this -// so we need to be careful not to commit too many resources. -// Currently, we only support one QUIC version at a time. -void QuicSocket::SendVersionNegotiation( - uint32_t version, - const QuicCID& dcid, - const QuicCID& scid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr) { - uint32_t sv[2]; - sv[0] = GenerateReservedVersion(remote_addr, version); - sv[1] = NGTCP2_PROTO_VER; - - uint8_t unused_random; - EntropySource(&unused_random, 1); - - size_t pktlen = dcid.length() + scid.length() + (sizeof(sv)) + 7; - - auto packet = QuicPacket::Create("version negotiation", pktlen); - ssize_t nwrite = ngtcp2_pkt_write_version_negotiation( - packet->data(), - NGTCP2_MAX_PKTLEN_IPV6, - unused_random, - dcid.data(), - dcid.length(), - scid.data(), - scid.length(), - sv, - arraysize(sv)); - if (nwrite <= 0) - return; - packet->set_length(nwrite); - SocketAddress remote_address(remote_addr); - SendPacket(local_addr, remote_address, std::move(packet)); -} - -// Possible generates and sends a stateless reset packet. -// This is terminal for the connection. It is possible -// that a malicious packet triggered this so we need to -// be careful not to commit too many resources. -bool QuicSocket::SendStatelessReset( - const QuicCID& cid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - size_t source_len) { - if (UNLIKELY(state_->stateless_reset_disabled)) - return false; - constexpr static size_t kRandlen = NGTCP2_MIN_STATELESS_RESET_RANDLEN * 5; - constexpr static size_t kMinStatelessResetLen = 41; - uint8_t random[kRandlen]; - - // Per the QUIC spec, we need to protect against sending too - // many stateless reset tokens to an endpoint to prevent - // endless looping. - if (GetCurrentStatelessResetCounter(remote_addr) >= - max_stateless_resets_per_host_) { - return false; - } - // Per the QUIC spec, a stateless reset token must be strictly - // smaller than the packet that triggered it. This is one of the - // mechanisms to prevent infinite looping exchange of stateless - // tokens with the peer. - // An endpoint should never send a stateless reset token smaller than - // 41 bytes per the QUIC spec. The reason is that packets less than - // 41 bytes may allow an observer to determine that it's a stateless - // reset. - size_t pktlen = source_len - 1; - if (pktlen < kMinStatelessResetLen) - return false; - - StatelessResetToken token(reset_token_secret_, cid); - EntropySource(random, kRandlen); - - auto packet = QuicPacket::Create("stateless reset", pktlen); - ssize_t nwrite = - ngtcp2_pkt_write_stateless_reset( - packet->data(), - NGTCP2_MAX_PKTLEN_IPV4, - const_cast<uint8_t*>(token.data()), - random, - kRandlen); - if (nwrite < static_cast<ssize_t>(kMinStatelessResetLen)) - return false; - packet->set_length(nwrite); - SocketAddress remote_address(remote_addr); - IncrementStatelessResetCounter(remote_address); - return SendPacket(local_addr, remote_address, std::move(packet)) == 0; -} - -// Generates and sends a retry packet. This is terminal -// for the connection. Retry packets are used to force -// explicit path validation by issuing a token to the -// peer that it must thereafter include in all subsequent -// initial packets. Upon receiving a retry packet, the -// peer must termination it's initial attempt to -// establish a connection and start a new attempt. -// -// Retry packets will only ever be generated by QUIC servers, -// and only if the QuicSocket is configured for explicit path -// validation. There is no way for a client to force a retry -// packet to be created. However, once a client determines that -// explicit path validation is enabled, it could attempt to -// DOS by sending a large number of malicious initial packets -// to intentionally ellicit retry packets (It can do so by -// intentionally sending initial packets that ignore the retry -// token). To help mitigate that risk, we limit the number of -// retries we send to a given remote endpoint. -bool QuicSocket::SendRetry( - const QuicCID& dcid, - const QuicCID& scid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr) { - auto info = addrLRU_.Upsert(remote_addr); - // Do not send a retry if the retry count is greater - // than the retry limit. - // TODO(@jasnell): Make the retry limit configurable. - if (++(info->retry_count) > DEFAULT_MAX_RETRY_LIMIT) - return true; - std::unique_ptr<QuicPacket> packet = - GenerateRetryPacket(token_secret_, dcid, scid, local_addr, remote_addr); - return packet ? - SendPacket(local_addr, remote_addr, std::move(packet)) == 0 : false; -} - -// Shutdown a connection prematurely, before a QuicSession is created. -// This should only be called t the start of a session before the crypto -// keys have been established. -void QuicSocket::ImmediateConnectionClose( - const QuicCID& scid, - const QuicCID& dcid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - int64_t reason) { - Debug(this, "Sending stateless connection close to %s", scid); - auto packet = QuicPacket::Create("immediate connection close"); - ssize_t nwrite = ngtcp2_crypto_write_connection_close( - packet->data(), - packet->length(), - scid.cid(), - dcid.cid(), - reason); - if (nwrite > 0) { - packet->set_length(nwrite); - SendPacket(local_addr, remote_addr, std::move(packet)); - } -} - -// Inspects the packet and possibly accepts it as a new -// initial packet creating a new QuicSession instance. -// If the packet is not acceptable, it is very important -// not to commit resources. -BaseObjectPtr<QuicSession> QuicSocket::AcceptInitialPacket( - uint32_t version, - const QuicCID& dcid, - const QuicCID& scid, - ssize_t nread, - const uint8_t* data, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags) { - HandleScope handle_scope(env()->isolate()); - Context::Scope context_scope(env()->context()); - ngtcp2_pkt_hd hd; - QuicCID ocid; - - // If the QuicSocket is not listening, the paket will be ignored. - if (!state_->server_listening) { - Debug(this, "QuicSocket is not listening"); - return {}; - } - - switch (ngtcp2_accept(&hd, data, static_cast<size_t>(nread))) { - case 1: - // Send Version Negotiation - SendVersionNegotiation(version, dcid, scid, local_addr, remote_addr); - // Fall through - case -1: - // Either a version negotiation packet was sent or the packet is - // an invalid initial packet. Either way, there's nothing more we - // can do here. - return {}; - } - - // If the server is busy, new connections will be shut down immediately - // after the initial keys are installed. The busy state is controlled - // entirely by local user code. It is important to understand that - // a QuicSession is created and resources are committed even though - // the QuicSession will be torn down as quickly as possible. - // Else, check to see if the number of connections total for this QuicSocket - // has been exceeded. If the count has been exceeded, shutdown the connection - // immediately after the initial keys are installed. - if (UNLIKELY(state_->server_busy == 1) || - sessions_.size() >= max_connections_ || - GetCurrentSocketAddressCounter(remote_addr) >= - max_connections_per_host_) { - Debug(this, "QuicSocket is busy or connection count exceeded"); - IncrementStat(&QuicSocketStats::server_busy_count); - ImmediateConnectionClose( - QuicCID(hd.scid), - QuicCID(hd.dcid), - local_addr, - remote_addr, - NGTCP2_CONNECTION_REFUSED); - return {}; - } - - // QUIC has address validation built in to the handshake but allows for - // an additional explicit validation request using RETRY frames. If we - // are using explicit validation, we check for the existence of a valid - // retry token in the packet. If one does not exist, we send a retry with - // a new token. If it does exist, and if it's valid, we grab the original - // cid and continue. - if (!is_validated_address(remote_addr)) { - switch (hd.type) { - case NGTCP2_PKT_INITIAL: - if (has_option_validate_address() || hd.token.len > 0) { - Debug(this, "Performing explicit address validation"); - if (hd.token.len == 0) { - Debug(this, "No retry token was detected. Generating one"); - SendRetry(dcid, scid, local_addr, remote_addr); - // Sending a retry token terminates this connection attempt. - return {}; - } - if (InvalidRetryToken( - hd.token, - remote_addr, - &ocid, - token_secret_, - retry_token_expiration_)) { - Debug(this, "Invalid retry token was detected. Failing"); - ImmediateConnectionClose( - QuicCID(hd.scid), - QuicCID(hd.dcid), - local_addr, - remote_addr); - return {}; - } - } - break; - case NGTCP2_PKT_0RTT: - SendRetry(dcid, scid, local_addr, remote_addr); - return {}; - } - } - - BaseObjectPtr<QuicSession> session = - QuicSession::CreateServer( - this, - server_session_config_, - local_addr, - remote_addr, - scid, - dcid, - ocid, - version, - server_alpn_, - server_options_, - qlog_); - CHECK(session); - - listener_->OnSessionReady(session); - - // It's possible that the session was destroyed while processing - // the ready callback. If it was, then we need to send an early - // CONNECTION_CLOSE. - if (session->is_destroyed()) { - ImmediateConnectionClose( - QuicCID(hd.scid), - QuicCID(hd.dcid), - local_addr, - remote_addr, - NGTCP2_CONNECTION_REFUSED); - } else { - session->set_wrapped(); - } - - return session; -} - -QuicSocket::SendWrap::SendWrap( - QuicState* quic_state, - Local<Object> req_wrap_obj, - size_t total_length) - : ReqWrap(quic_state->env(), req_wrap_obj, PROVIDER_QUICSOCKET), - total_length_(total_length), - quic_state_(quic_state) { -} - -std::string QuicSocket::SendWrap::MemoryInfoName() const { - return "QuicSendWrap"; -} - -void QuicSocket::SendWrap::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("session", session_); - tracker->TrackField("packet", packet_); -} - -int QuicSocket::SendPacket( - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - std::unique_ptr<QuicPacket> packet, - BaseObjectPtr<QuicSession> session) { - // If the packet is empty, there's nothing to do - if (packet->length() == 0) - return 0; - - Debug(this, "Sending %" PRIu64 " bytes to %s from %s (label: %s)", - packet->length(), - remote_addr, - local_addr, - packet->diagnostic_label()); - - // If DiagnosticPacketLoss returns true, it will call Done() internally - if (UNLIKELY(is_diagnostic_packet_loss(tx_loss_))) { - Debug(this, "Simulating transmitted packet loss"); - return 0; - } - - last_created_send_wrap_ = nullptr; - uv_buf_t buf = packet->buf(); - - auto endpoint = bound_endpoints_.find(local_addr); - CHECK_NE(endpoint, bound_endpoints_.end()); - int err = endpoint->second->Send(&buf, 1, remote_addr.data()); - - if (err != 0) { - if (err > 0) err = 0; - OnSend(err, packet.get()); - } else { - CHECK_NOT_NULL(last_created_send_wrap_); - last_created_send_wrap_->set_packet(std::move(packet)); - if (session) - last_created_send_wrap_->set_session(session); - } - return err; -} - -void QuicSocket::OnSend(int status, QuicPacket* packet) { - if (status == 0) { - Debug(this, "Sent %" PRIu64 " bytes (label: %s)", - packet->length(), - packet->diagnostic_label()); - IncrementStat(&QuicSocketStats::bytes_sent, packet->length()); - IncrementStat(&QuicSocketStats::packets_sent); - } else { - Debug(this, "Failed to send %" PRIu64 " bytes (status: %d, label: %s)", - packet->length(), - status, - packet->diagnostic_label()); - } -} - -void QuicSocket::OnSendDone(ReqWrap<uv_udp_send_t>* wrap, int status) { - std::unique_ptr<SendWrap> req_wrap(static_cast<SendWrap*>(wrap)); - OnSend(status, req_wrap->packet()); -} - -void QuicSocket::CheckAllocatedSize(size_t previous_size) const { - CHECK_GE(current_ngtcp2_memory_, previous_size); -} - -void QuicSocket::IncreaseAllocatedSize(size_t size) { - current_ngtcp2_memory_ += size; -} - -void QuicSocket::DecreaseAllocatedSize(size_t size) { - current_ngtcp2_memory_ -= size; -} - -void QuicSocket::PushListener(QuicSocketListener* listener) { - CHECK_NOT_NULL(listener); - CHECK(!listener->socket_); - - listener->previous_listener_ = listener_; - listener->socket_.reset(this); - - listener_ = listener; -} - -void QuicSocket::RemoveListener(QuicSocketListener* listener) { - CHECK_NOT_NULL(listener); - - QuicSocketListener* previous; - QuicSocketListener* current; - - for (current = listener_, previous = nullptr; - /* No loop condition because we want a crash if listener is not found */ - ; previous = current, current = current->previous_listener_) { - CHECK_NOT_NULL(current); - if (current == listener) { - if (previous != nullptr) - previous->previous_listener_ = current->previous_listener_; - else - listener_ = listener->previous_listener_; - break; - } - } - - listener->socket_.reset(); - listener->previous_listener_ = nullptr; -} - -bool QuicSocket::SocketAddressInfoTraits::CheckExpired( - const SocketAddress& address, - const Type& type) { - return (uv_hrtime() - type.timestamp) > 1e10; // 10 seconds. -} - -void QuicSocket::SocketAddressInfoTraits::Touch( - const SocketAddress& address, - Type* type) { - type->timestamp = uv_hrtime(); -} - -// JavaScript API -namespace { -void NewQuicEndpoint(const FunctionCallbackInfo<Value>& args) { - QuicState* state = Environment::GetBindingData<QuicState>(args); - CHECK(args.IsConstructCall()); - CHECK(args[0]->IsObject()); - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args[0].As<Object>()); - CHECK(args[1]->IsObject()); - CHECK_GE(args[1].As<Object>()->InternalFieldCount(), - UDPWrapBase::kUDPWrapBaseField); - new QuicEndpoint(state, args.This(), socket, args[1].As<Object>()); -} - -void NewQuicSocket(const FunctionCallbackInfo<Value>& args) { - QuicState* state = Environment::GetBindingData<QuicState>(args); - Environment* env = state->env(); - CHECK(args.IsConstructCall()); - - uint32_t options; - uint32_t retry_token_expiration; - uint32_t max_connections; - uint32_t max_connections_per_host; - uint32_t max_stateless_resets_per_host; - - if (!args[0]->Uint32Value(env->context()).To(&options) || - !args[1]->Uint32Value(env->context()).To(&retry_token_expiration) || - !args[2]->Uint32Value(env->context()).To(&max_connections) || - !args[3]->Uint32Value(env->context()).To(&max_connections_per_host) || - !args[4]->Uint32Value(env->context()) - .To(&max_stateless_resets_per_host)) { - return; - } - CHECK_GE(retry_token_expiration, MIN_RETRYTOKEN_EXPIRATION); - CHECK_LE(retry_token_expiration, MAX_RETRYTOKEN_EXPIRATION); - - const uint8_t* session_reset_secret = nullptr; - if (args[6]->IsArrayBufferView()) { - ArrayBufferViewContents<uint8_t> buf(args[6].As<ArrayBufferView>()); - CHECK_EQ(buf.length(), kTokenSecretLen); - session_reset_secret = buf.data(); - } - - new QuicSocket( - state, - args.This(), - retry_token_expiration, - max_connections, - max_connections_per_host, - max_stateless_resets_per_host, - options, - args[5]->IsTrue() ? QlogMode::kEnabled : QlogMode::kDisabled, - session_reset_secret, - args[7]->IsTrue()); -} - -void QuicSocketAddEndpoint(const FunctionCallbackInfo<Value>& args) { - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); - CHECK(args[0]->IsObject()); - QuicEndpoint* endpoint; - ASSIGN_OR_RETURN_UNWRAP(&endpoint, args[0].As<Object>()); - socket->AddEndpoint( - BaseObjectPtr<QuicEndpoint>(endpoint), - args[1]->IsTrue()); -} - -// Enabling diagnostic packet loss enables a mode where the QuicSocket -// instance will randomly ignore received packets in order to simulate -// packet loss. This is not an API that should be enabled in production -// but is useful when debugging and diagnosing performance issues. -// Diagnostic packet loss is enabled by setting either the tx or rx -// arguments to a value between 0.0 and 1.0. Setting both values to 0.0 -// disables the mechanism. -void QuicSocketSetDiagnosticPacketLoss( - const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); - double rx, tx; - if (!args[0]->NumberValue(env->context()).To(&rx) || - !args[1]->NumberValue(env->context()).To(&tx)) return; - CHECK_GE(rx, 0.0f); - CHECK_GE(tx, 0.0f); - CHECK_LE(rx, 1.0f); - CHECK_LE(tx, 1.0f); - socket->set_diagnostic_packet_loss(rx, tx); -} - -void QuicSocketDestroy(const FunctionCallbackInfo<Value>& args) { - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); - socket->ReceiveStop(); -} - -void QuicSocketListen(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder(), - args.GetReturnValue().Set(UV_EBADF)); - CHECK(args[0]->IsObject() && - env->secure_context_constructor_template()->HasInstance(args[0])); - SecureContext* sc; - ASSIGN_OR_RETURN_UNWRAP(&sc, args[0].As<Object>(), - args.GetReturnValue().Set(UV_EBADF)); - - sockaddr_storage preferred_address_storage; - const sockaddr* preferred_address = nullptr; - if (args[1]->IsString()) { - node::Utf8Value preferred_address_host(args.GetIsolate(), args[1]); - int32_t preferred_address_family; - uint32_t preferred_address_port; - if (!args[2]->Int32Value(env->context()).To(&preferred_address_family) || - !args[3]->Uint32Value(env->context()).To(&preferred_address_port)) - return; - if (SocketAddress::ToSockAddr( - preferred_address_family, - *preferred_address_host, - preferred_address_port, - &preferred_address_storage)) { - preferred_address = - reinterpret_cast<const sockaddr*>(&preferred_address_storage); - } - } - - std::string alpn(NGHTTP3_ALPN_H3); - if (args[4]->IsString()) { - Utf8Value val(env->isolate(), args[4]); - alpn = val.length(); - alpn += *val; - } - - uint32_t options = 0; - if (!args[5]->Uint32Value(env->context()).To(&options)) return; - - socket->Listen( - BaseObjectPtr<SecureContext>(sc), - preferred_address, - alpn, - options); -} - -void QuicEndpointWaitForPendingCallbacks( - const FunctionCallbackInfo<Value>& args) { - QuicEndpoint* endpoint; - ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.Holder()); - endpoint->WaitForPendingCallbacks(); -} - -} // namespace - -void QuicEndpoint::Initialize( - Environment* env, - Local<Object> target, - Local<Context> context) { - Isolate* isolate = env->isolate(); - Local<FunctionTemplate> endpoint = env->NewFunctionTemplate(NewQuicEndpoint); - endpoint->Inherit(BaseObject::GetConstructorTemplate(env)); - endpoint->InstanceTemplate()->SetInternalFieldCount( - QuicEndpoint::kInternalFieldCount); - env->SetProtoMethod(endpoint, - "waitForPendingCallbacks", - QuicEndpointWaitForPendingCallbacks); - endpoint->InstanceTemplate()->Set(env->owner_symbol(), Null(isolate)); - - env->SetConstructorFunction(target, "QuicEndpoint", endpoint); -} - -void QuicSocket::Initialize( - Environment* env, - Local<Object> target, - Local<Context> context) { - Isolate* isolate = env->isolate(); - Local<FunctionTemplate> socket = env->NewFunctionTemplate(NewQuicSocket); - socket->Inherit(AsyncWrap::GetConstructorTemplate(env)); - socket->InstanceTemplate()->SetInternalFieldCount( - QuicSocket::kInternalFieldCount); - socket->InstanceTemplate()->Set(env->owner_symbol(), Null(isolate)); - env->SetProtoMethod(socket, - "addEndpoint", - QuicSocketAddEndpoint); - env->SetProtoMethod(socket, - "destroy", - QuicSocketDestroy); - env->SetProtoMethod(socket, - "listen", - QuicSocketListen); - env->SetProtoMethod(socket, - "setDiagnosticPacketLoss", - QuicSocketSetDiagnosticPacketLoss); - socket->Inherit(HandleWrap::GetConstructorTemplate(env)); - env->SetConstructorFunction(target, "QuicSocket", socket); - - Local<FunctionTemplate> sendwrap_ctor = FunctionTemplate::New(isolate); - sendwrap_ctor->Inherit(AsyncWrap::GetConstructorTemplate(env)); - sendwrap_ctor->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "SendWrap")); - Local<ObjectTemplate> sendwrap_template = sendwrap_ctor->InstanceTemplate(); - sendwrap_template->SetInternalFieldCount(SendWrap::kInternalFieldCount); - env->set_quicsocketsendwrap_instance_template(sendwrap_template); -} - -} // namespace quic -} // namespace node diff --git a/src/quic/node_quic_socket.h b/src/quic/node_quic_socket.h deleted file mode 100644 index 63bb49091b1..00000000000 --- a/src/quic/node_quic_socket.h +++ /dev/null @@ -1,609 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_SOCKET_H_ -#define SRC_QUIC_NODE_QUIC_SOCKET_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "aliased_struct.h" -#include "base_object.h" -#include "node.h" -#include "node_crypto.h" -#include "node_internals.h" -#include "ngtcp2/ngtcp2.h" -#include "nghttp3/nghttp3.h" -#include "node_quic_state.h" -#include "node_quic_session.h" -#include "node_quic_util.h" -#include "node_sockaddr.h" -#include "env.h" -#include "udp_wrap.h" -#include "v8.h" -#include "uv.h" - -#include <deque> -#include <map> -#include <string> -#include <vector> - -namespace node { - -using v8::Context; -using v8::Local; -using v8::Object; - -namespace quic { - -class QuicSocket; -class QuicEndpoint; - -constexpr size_t DEFAULT_MAX_SOCKETADDRESS_LRU_SIZE = 1000; -constexpr size_t DEFAULT_MAX_RETRY_LIMIT = 10; - -#define QUICSOCKET_OPTIONS(V) \ - V(VALIDATE_ADDRESS, validate_address) - -#define V(id, _) QUICSOCKET_OPTIONS_##id, -enum QuicSocketOptions : uint32_t { - QUICSOCKET_OPTIONS(V) - QUICSOCKET_OPTIONS_COUNT -}; -#undef V - -#define QUICSOCKET_SHARED_STATE(V) \ - V(SERVER_LISTENING, server_listening, uint8_t) \ - V(SERVER_BUSY, server_busy, uint8_t) \ - V(STATELESS_RESET_DISABLED, stateless_reset_disabled, uint8_t) - -#define V(_, name, type) type name; -struct QuicSocketState { - QUICSOCKET_SHARED_STATE(V) -}; -#undef V - -#define V(id, name, _) \ - IDX_QUICSOCKET_STATE_##id = offsetof(QuicSocketState, name), -enum QuicSocketStateFields { - QUICSOCKET_SHARED_STATE(V) - IDX_QUICSOCKET_STATE_END -}; -#undef V - -#define SOCKET_STATS(V) \ - V(CREATED_AT, created_at, "Created At") \ - V(BOUND_AT, bound_at, "Bound At") \ - V(LISTEN_AT, listen_at, "Listen At") \ - V(DESTROYED_AT, destroyed_at, "Destroyed At") \ - V(BYTES_RECEIVED, bytes_received, "Bytes Received") \ - V(BYTES_SENT, bytes_sent, "Bytes Sent") \ - V(PACKETS_RECEIVED, packets_received, "Packets Received") \ - V(PACKETS_IGNORED, packets_ignored, "Packets Ignored") \ - V(PACKETS_SENT, packets_sent, "Packets Sent") \ - V(SERVER_SESSIONS, server_sessions, "Server Sessions") \ - V(CLIENT_SESSIONS, client_sessions, "Client Sessions") \ - V(STATELESS_RESET_COUNT, stateless_reset_count, "Stateless Reset Count") \ - V(SERVER_BUSY_COUNT, server_busy_count, "Server Busy Count") - -#define V(name, _, __) IDX_QUIC_SOCKET_STATS_##name, -enum QuicSocketStatsIdx : int { - SOCKET_STATS(V) - IDX_QUIC_SOCKET_STATS_COUNT -}; -#undef V - -#define V(_, name, __) uint64_t name; -struct QuicSocketStats { - SOCKET_STATS(V) -}; -#undef V - -struct QuicSocketStatsTraits { - using Stats = QuicSocketStats; - using Base = QuicSocket; - - template <typename Fn> - static void ToString(const Base& ptr, Fn&& add_field); -}; - -// This is the generic interface for objects that control QuicSocket -// instances. The default `JSQuicSocketListener` emits events to -// JavaScript -class QuicSocketListener { - public: - virtual ~QuicSocketListener(); - - virtual void OnError(ssize_t code); - virtual void OnSessionReady(BaseObjectPtr<QuicSession> session); - virtual void OnServerBusy(); - virtual void OnEndpointDone(QuicEndpoint* endpoint); - virtual void OnDestroy(); - - QuicSocket* socket() const { return socket_.get(); } - - private: - BaseObjectWeakPtr<QuicSocket> socket_; - QuicSocketListener* previous_listener_ = nullptr; - friend class QuicSocket; -}; - -class JSQuicSocketListener final : public QuicSocketListener { - public: - void OnError(ssize_t code) override; - void OnSessionReady(BaseObjectPtr<QuicSession> session) override; - void OnServerBusy() override; - void OnEndpointDone(QuicEndpoint* endpoint) override; - void OnDestroy() override; -}; - -// This is just a formality as the QUIC spec normatively -// defines that the ipv4 max pktlen is always going to be -// larger than the ipv6 max pktlen, but in the off chance -// ever changes (which is unlikely) we check here. -constexpr size_t MAX_PKTLEN = - std::max<size_t>(NGTCP2_MAX_PKTLEN_IPV4, NGTCP2_MAX_PKTLEN_IPV6); - -// A serialized QuicPacket to be sent by a QuicSocket instance. -// QuicPackets are intended to be transient. They are created, -// filled with the contents of a serialized packet, and passed -// off immediately to the QuicSocket to be sent. As soon as -// the packet is sent, it is freed. -class QuicPacket : public MemoryRetainer { - public: - // Creates a new QuicPacket. By default the packet will be - // stack allocated with a max size of NGTCP2_MAX_PKTLEN_IPV4. - // If a larger packet size is specified, it will be heap - // allocated. A QUIC packet should never be larger than the - // current MTU to avoid IP fragmentation. - // - // The content of a QuicPacket is provided by ngtcp2 and is - // opaque for us. The typical use pattern is to create a QuicPacket - // instance and then pass a pointer to it's internal buffer and max - // size in to an ngtcp2 function that serializes the data. - // ngtcp2 will fill the buffer as much as possible then return - // the number of bytes serialized. User code is then responsible - // for calling set_length() to set the final length of the - // QuicPacket prior to sending it off to the QuicSocket. - // - // The diagnostic label is used in NODE_DEBUG_NATIVE output - // to differentiate send operations. This should always be - // a statically allocated string or nullptr (in which case - // the value "unspecified" is used in the debug output). - // - // Instances of std::unique_ptr<QuicPacket> are moved through - // QuicSocket and ultimately become the responsibility of the - // SendWrap instance. When the SendWrap is cleaned up, the - // QuicPacket instance will be freed. - static inline std::unique_ptr<QuicPacket> Create( - const char* diagnostic_label = nullptr, - size_t len = MAX_PKTLEN); - - // Copy the data of the QuicPacket to a new one. Currently, - // this is only used when retransmitting close connection - // packets from a QuicServer. - static inline std::unique_ptr<QuicPacket> Copy( - const std::unique_ptr<QuicPacket>& other); - - QuicPacket(const char* diagnostic_label, size_t len); - QuicPacket(const QuicPacket& other); - - uint8_t* data() { return data_; } - - size_t length() const { return len_; } - - uv_buf_t buf() const { - return uv_buf_init( - const_cast<char*>(reinterpret_cast<const char*>(&data_)), - length()); - } - - inline void set_length(size_t len); - - const char* diagnostic_label() const; - - SET_NO_MEMORY_INFO(); - SET_MEMORY_INFO_NAME(QuicPacket); - SET_SELF_SIZE(QuicPacket); - - private: - uint8_t data_[MAX_PKTLEN]; - size_t len_ = MAX_PKTLEN; - const char* diagnostic_label_ = nullptr; -}; - -// QuicEndpointListener listens to events generated by a QuicEndpoint. -class QuicEndpointListener { - public: - virtual void OnError(QuicEndpoint* endpoint, ssize_t error) = 0; - virtual void OnReceive( - ssize_t nread, - AllocatedBuffer buf, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags) = 0; - virtual ReqWrap<uv_udp_send_t>* OnCreateSendWrap(size_t msg_size) = 0; - virtual void OnSendDone(ReqWrap<uv_udp_send_t>* wrap, int status) = 0; - virtual void OnBind(QuicEndpoint* endpoint) = 0; - virtual void OnEndpointDone(QuicEndpoint* endpoint) = 0; -}; - -// A QuicEndpoint wraps a UDPBaseWrap. A single QuicSocket may -// have multiple QuicEndpoints, the lifecycles of which are -// attached to the QuicSocket. -class QuicEndpoint final : public BaseObject, - public UDPListener { - public: - static void Initialize( - Environment* env, - Local<Object> target, - Local<Context> context); - - QuicEndpoint( - QuicState* quic_state, - Local<Object> wrap, - QuicSocket* listener, - Local<Object> udp_wrap); - - ~QuicEndpoint() override; - - SocketAddress local_address() const { - return udp_->GetSockName(); - } - - // Implementation for UDPListener - uv_buf_t OnAlloc(size_t suggested_size) override; - - void OnRecv(ssize_t nread, - const uv_buf_t& buf, - const sockaddr* addr, - unsigned int flags) override; - - ReqWrap<uv_udp_send_t>* CreateSendWrap(size_t msg_size) override; - - void OnSendDone(ReqWrap<uv_udp_send_t>* wrap, int status) override; - - void OnAfterBind() override; - - inline int ReceiveStart(); - - inline int ReceiveStop(); - - inline int Send( - uv_buf_t* buf, - size_t len, - const sockaddr* addr); - - void IncrementPendingCallbacks() { pending_callbacks_++; } - void DecrementPendingCallbacks() { pending_callbacks_--; } - bool has_pending_callbacks() const { return pending_callbacks_ > 0; } - inline void WaitForPendingCallbacks(); - - QuicState* quic_state() const { return quic_state_.get(); } - - SET_NO_MEMORY_INFO(); - SET_MEMORY_INFO_NAME(QuicEndpoint) - SET_SELF_SIZE(QuicEndpoint) - - private: - BaseObjectWeakPtr<QuicSocket> listener_; - UDPWrapBase* udp_; - BaseObjectPtr<AsyncWrap> strong_ptr_; - size_t pending_callbacks_ = 0; - bool waiting_for_callbacks_ = false; - BaseObjectPtr<QuicState> quic_state_; -}; - -// QuicSocket manages the flow of data from the UDP socket to the -// QuicSession. It is responsible for managing the lifecycle of the -// UDP sockets, listening for new server QuicSession instances, and -// passing data two and from the remote peer. -class QuicSocket : public AsyncWrap, - public QuicEndpointListener, - public mem::NgLibMemoryManager<QuicSocket, ngtcp2_mem>, - public StatsBase<QuicSocketStatsTraits> { - public: - static void Initialize( - Environment* env, - Local<Object> target, - Local<Context> context); - - QuicSocket( - QuicState* quic_state, - Local<Object> wrap, - // A retry token should only be valid for a small window of time. - // The retry_token_expiration specifies the number of seconds a - // retry token is permitted to be valid. - uint64_t retry_token_expiration, - // To prevent malicious clients from opening too many concurrent - // connections, we limit the maximum number per remote sockaddr. - size_t max_connections, - size_t max_connections_per_host, - size_t max_stateless_resets_per_host - = DEFAULT_MAX_STATELESS_RESETS_PER_HOST, - uint32_t options = 0, - QlogMode qlog = QlogMode::kDisabled, - const uint8_t* session_reset_secret = nullptr, - bool disable_session_reset = false); - - ~QuicSocket() override; - - // Returns the default/preferred local address. Additional - // QuicEndpoint instances may be associated with the - // QuicSocket bound to other local addresses. - inline SocketAddress local_address() const; - - void MaybeClose(); - - inline void AddSession( - const QuicCID& cid, - BaseObjectPtr<QuicSession> session); - - inline void AssociateCID( - const QuicCID& cid, - const QuicCID& scid); - - inline void DisassociateCID( - const QuicCID& cid); - - inline void AssociateStatelessResetToken( - const StatelessResetToken& token, - BaseObjectPtr<QuicSession> session); - - inline void DisassociateStatelessResetToken( - const StatelessResetToken& token); - - void Listen( - BaseObjectPtr<crypto::SecureContext> context, - const sockaddr* preferred_address = nullptr, - const std::string& alpn = NGHTTP3_ALPN_H3, - uint32_t options = 0); - - inline void ReceiveStart(); - - inline void ReceiveStop(); - - inline void RemoveSession( - const QuicCID& cid, - const SocketAddress& addr); - - inline void ReportSendError(int error); - - int SendPacket( - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - std::unique_ptr<QuicPacket> packet, - BaseObjectPtr<QuicSession> session = BaseObjectPtr<QuicSession>()); - -#define V(id, name) \ - bool has_option_##name() const { \ - return options_ & (1 << QUICSOCKET_OPTIONS_##id); } - QUICSOCKET_OPTIONS(V) -#undef V - - // Allows the server busy status to be enabled from C++. A notification - // will be sent to the JavaScript side informing that the status has - // changed. - inline void ServerBusy(bool on); - - inline void set_diagnostic_packet_loss(double rx = 0.0, double tx = 0.0); - - BaseObjectPtr<crypto::SecureContext> server_secure_context() const { - return server_secure_context_; - } - - QuicState* quic_state() { return quic_state_.get(); } - - void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(QuicSocket) - SET_SELF_SIZE(QuicSocket) - - // Implementation for mem::NgLibMemoryManager - void CheckAllocatedSize(size_t previous_size) const; - - void IncreaseAllocatedSize(size_t size); - - void DecreaseAllocatedSize(size_t size); - - const uint8_t* session_reset_secret() { return reset_token_secret_; } - - // Implementation for QuicListener - ReqWrap<uv_udp_send_t>* OnCreateSendWrap(size_t msg_size) override; - - // Implementation for QuicListener - void OnSendDone(ReqWrap<uv_udp_send_t>* wrap, int status) override; - - // Implementation for QuicListener - void OnBind(QuicEndpoint* endpoint) override; - - // Implementation for QuicListener - void OnReceive( - ssize_t nread, - AllocatedBuffer buf, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags) override; - - // Implementation for QuicListener - void OnError(QuicEndpoint* endpoint, ssize_t error) override; - - // Implementation for QuicListener - void OnEndpointDone(QuicEndpoint* endpoint) override; - - // Serializes and transmits a RETRY packet to the connected peer. - bool SendRetry( - const QuicCID& dcid, - const QuicCID& scid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr); - - // Serializes and transmits a Stateless Reset to the connected peer. - bool SendStatelessReset( - const QuicCID& cid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - size_t source_len); - - // Serializes and transmits a Version Negotiation packet to the - // connected peer. - void SendVersionNegotiation( - uint32_t version, - const QuicCID& dcid, - const QuicCID& scid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr); - - void PushListener(QuicSocketListener* listener); - - void RemoveListener(QuicSocketListener* listener); - - inline void AddEndpoint( - BaseObjectPtr<QuicEndpoint> endpoint, - bool preferred = false); - - void ImmediateConnectionClose( - const QuicCID& scid, - const QuicCID& dcid, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - int64_t reason = NGTCP2_INVALID_TOKEN); - - private: - static void OnAlloc( - uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf); - - void OnSend(int status, QuicPacket* packet); - - inline void set_validated_address(const SocketAddress& addr); - - inline bool is_validated_address(const SocketAddress& addr) const; - - bool MaybeStatelessReset( - const QuicCID& dcid, - const QuicCID& scid, - ssize_t nread, - const uint8_t* data, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags); - - BaseObjectPtr<QuicSession> AcceptInitialPacket( - uint32_t version, - const QuicCID& dcid, - const QuicCID& scid, - ssize_t nread, - const uint8_t* data, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - unsigned int flags); - - BaseObjectPtr<QuicSession> FindSession(const QuicCID& cid); - - inline void IncrementSocketAddressCounter(const SocketAddress& addr); - - inline void DecrementSocketAddressCounter(const SocketAddress& addr); - - inline void IncrementStatelessResetCounter(const SocketAddress& addr); - - inline size_t GetCurrentSocketAddressCounter(const SocketAddress& addr); - - inline size_t GetCurrentStatelessResetCounter(const SocketAddress& addr); - - // Returns true if, and only if, diagnostic packet loss is enabled - // and the current packet should be artificially considered lost. - inline bool is_diagnostic_packet_loss(double prob) const; - - ngtcp2_mem alloc_info_; - - std::vector<BaseObjectPtr<QuicEndpoint>> endpoints_; - SocketAddress::Map<BaseObjectWeakPtr<QuicEndpoint>> bound_endpoints_; - BaseObjectWeakPtr<QuicEndpoint> preferred_endpoint_; - BaseObjectPtr<SocketAddressBlockListWrap> block_list_; - - uint32_t flags_ = 0; - uint32_t options_ = 0; - uint32_t server_options_; - - AliasedStruct<QuicSocketState> state_; - - size_t max_connections_ = DEFAULT_MAX_CONNECTIONS; - size_t max_connections_per_host_ = DEFAULT_MAX_CONNECTIONS_PER_HOST; - size_t current_ngtcp2_memory_ = 0; - size_t max_stateless_resets_per_host_ = DEFAULT_MAX_STATELESS_RESETS_PER_HOST; - - uint64_t retry_token_expiration_; - - // Used to specify diagnostic packet loss probabilities - double rx_loss_ = 0.0; - double tx_loss_ = 0.0; - - QuicSocketListener* listener_; - JSQuicSocketListener default_listener_; - QuicSessionConfig server_session_config_; - QlogMode qlog_ = QlogMode::kDisabled; - BaseObjectPtr<crypto::SecureContext> server_secure_context_; - std::string server_alpn_; - QuicCID::Map<BaseObjectPtr<QuicSession>> sessions_; - QuicCID::Map<QuicCID> dcid_to_scid_; - - uint8_t token_secret_[kTokenSecretLen]; - uint8_t reset_token_secret_[NGTCP2_STATELESS_RESET_TOKENLEN]; - - struct SocketAddressInfo { - size_t active_connections; - size_t reset_count; - size_t retry_count; - bool validated; - uint64_t timestamp; - }; - - struct SocketAddressInfoTraits { - using Type = SocketAddressInfo; - - static bool CheckExpired(const SocketAddress& address, const Type& type); - static void Touch(const SocketAddress& address, Type* type); - }; - - SocketAddressLRU<SocketAddressInfoTraits> addrLRU_; - - StatelessResetToken::Map<QuicSession> token_map_; - - class SendWrap : public ReqWrap<uv_udp_send_t> { - public: - SendWrap(QuicState* quic_state, - v8::Local<v8::Object> req_wrap_obj, - size_t total_length_); - - void set_packet(std::unique_ptr<QuicPacket> packet) { - packet_ = std::move(packet); - } - - QuicPacket* packet() { return packet_.get(); } - - void set_session(BaseObjectPtr<QuicSession> session) { session_ = session; } - - size_t total_length() const { return total_length_; } - - QuicState* quic_state() { return quic_state_.get(); } - - SET_SELF_SIZE(SendWrap); - std::string MemoryInfoName() const override; - void MemoryInfo(MemoryTracker* tracker) const override; - - private: - BaseObjectPtr<QuicSession> session_; - std::unique_ptr<QuicPacket> packet_; - size_t total_length_; - BaseObjectPtr<QuicState> quic_state_; - }; - - SendWrap* last_created_send_wrap_ = nullptr; - BaseObjectPtr<QuicState> quic_state_; - - friend class QuicSocketListener; -}; - -} // namespace quic -} // namespace node - -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_SOCKET_H_ diff --git a/src/quic/node_quic_state.h b/src/quic/node_quic_state.h deleted file mode 100644 index 6eb76529f89..00000000000 --- a/src/quic/node_quic_state.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_STATE_H_ -#define SRC_QUIC_NODE_QUIC_STATE_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "aliased_buffer.h" - -namespace node { -namespace quic { - -enum QuicSessionConfigIndex : int { - IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT, - IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL, - IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE, - IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI, - IDX_QUIC_SESSION_MAX_DATA, - IDX_QUIC_SESSION_MAX_STREAMS_BIDI, - IDX_QUIC_SESSION_MAX_STREAMS_UNI, - IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT, - IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE, - IDX_QUIC_SESSION_ACK_DELAY_EXPONENT, - IDX_QUIC_SESSION_DISABLE_MIGRATION, - IDX_QUIC_SESSION_MAX_ACK_DELAY, - IDX_QUIC_SESSION_CC_ALGO, - IDX_QUIC_SESSION_CONFIG_COUNT -}; - -enum Http3ConfigIndex : int { - IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, - IDX_HTTP3_QPACK_BLOCKED_STREAMS, - IDX_HTTP3_MAX_HEADER_LIST_SIZE, - IDX_HTTP3_MAX_PUSHES, - IDX_HTTP3_MAX_HEADER_PAIRS, - IDX_HTTP3_MAX_HEADER_LENGTH, - IDX_HTTP3_CONFIG_COUNT -}; - -class QuicState : public BaseObject { - public: - explicit QuicState(Environment* env, v8::Local<v8::Object> obj) - : BaseObject(env, obj), - root_buffer( - env->isolate(), - sizeof(quic_state_internal)), - quicsessionconfig_buffer( - env->isolate(), - offsetof(quic_state_internal, quicsessionconfig_buffer), - IDX_QUIC_SESSION_CONFIG_COUNT + 1, - root_buffer), - http3config_buffer( - env->isolate(), - offsetof(quic_state_internal, http3config_buffer), - IDX_HTTP3_CONFIG_COUNT + 1, - root_buffer) { - } - - AliasedUint8Array root_buffer; - AliasedFloat64Array quicsessionconfig_buffer; - AliasedFloat64Array http3config_buffer; - - bool warn_trace_tls = true; - - static constexpr FastStringKey binding_data_name { "quic" }; - - void MemoryInfo(MemoryTracker* tracker) const override; - SET_SELF_SIZE(QuicState) - SET_MEMORY_INFO_NAME(QuicState) - - private: - struct quic_state_internal { - // doubles first so that they are always sizeof(double)-aligned - double quicsessionconfig_buffer[IDX_QUIC_SESSION_CONFIG_COUNT + 1]; - double http3config_buffer[IDX_HTTP3_CONFIG_COUNT + 1]; - }; -}; - -} // namespace quic -} // namespace node - -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_STATE_H_ diff --git a/src/quic/node_quic_stream-inl.h b/src/quic/node_quic_stream-inl.h deleted file mode 100644 index e79a326359c..00000000000 --- a/src/quic/node_quic_stream-inl.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_STREAM_INL_H_ -#define SRC_QUIC_NODE_QUIC_STREAM_INL_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "debug_utils-inl.h" -#include "node_quic_session.h" -#include "node_quic_stream.h" -#include "node_quic_buffer-inl.h" - -namespace node { -namespace quic { - -QuicStreamDirection QuicStream::direction() const { - return stream_id_ & 0b10 ? - QUIC_STREAM_UNIDIRECTIONAL : - QUIC_STREAM_BIRECTIONAL; -} - -QuicStreamOrigin QuicStream::origin() const { - return stream_id_ & 0b01 ? - QUIC_STREAM_SERVER : - QUIC_STREAM_CLIENT; -} - -void QuicStream::set_final_size(uint64_t final_size) { - // Only set the final size once. - if (state_->fin_received == 1) { - CHECK_LE(final_size, GetStat(&QuicStreamStats::final_size)); - return; - } - state_->fin_received = 1; - SetStat(&QuicStreamStats::final_size, final_size); -} - -bool QuicStream::was_ever_writable() const { - if (direction() == QUIC_STREAM_UNIDIRECTIONAL) { - return session_->is_server() ? - origin() == QUIC_STREAM_SERVER : - origin() == QUIC_STREAM_CLIENT; - } - return true; -} - -bool QuicStream::is_writable() const { - return was_ever_writable() && !streambuf_.is_ended(); -} - -bool QuicStream::was_ever_readable() const { - if (direction() == QUIC_STREAM_UNIDIRECTIONAL) { - return session_->is_server() ? - origin() == QUIC_STREAM_CLIENT : - origin() == QUIC_STREAM_SERVER; - } - - return true; -} - -void QuicStream::set_fin_sent() { - Debug(this, "final stream frame sent"); - state_->fin_sent = 1; - if (shutdown_done_ != nullptr) { - shutdown_done_(0); - } -} - -void QuicStream::set_destroyed() { - destroyed_ = true; -} - -bool QuicStream::is_readable() const { - return was_ever_readable() && state_->read_ended == 0; -} - -bool QuicStream::is_write_finished() const { - return state_->fin_sent == 1 && streambuf_.length() == 0; -} - -bool QuicStream::SubmitInformation(v8::Local<v8::Array> headers) { - return session_->SubmitInformation(stream_id_, headers); -} - -bool QuicStream::SubmitHeaders(v8::Local<v8::Array> headers, uint32_t flags) { - return session_->SubmitHeaders(stream_id_, headers, flags); -} - -bool QuicStream::SubmitTrailers(v8::Local<v8::Array> headers) { - return session_->SubmitTrailers(stream_id_, headers); -} - -BaseObjectPtr<QuicStream> QuicStream::SubmitPush( - v8::Local<v8::Array> headers) { - return session_->SubmitPush(stream_id_, headers); -} - -void QuicStream::EndHeaders(int64_t push_id) { - Debug(this, "End Headers"); - // Upon completion of a block of headers, convert the - // vector of Header objects into an array of name+value - // pairs, then call the on_stream_headers function. - session()->application()->StreamHeaders( - stream_id_, - headers_kind_, - headers_, - push_id); - headers_.clear(); -} - -void QuicStream::set_headers_kind(QuicStreamHeadersKind kind) { - headers_kind_ = kind; -} - -void QuicStream::BeginHeaders(QuicStreamHeadersKind kind) { - Debug(this, "Beginning Headers"); - // Upon start of a new block of headers, ensure that any - // previously collected ones are cleaned up. - headers_.clear(); - set_headers_kind(kind); -} - -void QuicStream::Commit(size_t amount) { - CHECK(!is_destroyed()); - streambuf_.Seek(amount); -} - -// ResetStream will cause ngtcp2 to queue a RESET_STREAM and STOP_SENDING -// frame, as appropriate, for the given stream_id. For a locally-initiated -// unidirectional stream, only a RESET_STREAM frame will be scheduled and -// the stream will be immediately closed. For a bidirectional stream, a -// STOP_SENDING frame will be sent. -void QuicStream::ResetStream(uint64_t app_error_code) { - QuicSession::SendSessionScope send_scope(session()); - session()->ShutdownStream(id(), app_error_code); - state_->read_ended = 1; - streambuf_.Cancel(); - streambuf_.End(); -} - -// StopSending will cause ngtcp2 to queue a STOP_SENDING frame if the -// stream is still inbound readable. -void QuicStream::StopSending(uint64_t app_error_code) { - QuicSession::SendSessionScope send_scope(session()); - ngtcp2_conn_shutdown_stream_read( - session()->connection(), - stream_id_, - app_error_code); - state_->read_ended = 1; -} - -void QuicStream::CancelPendingWrites() { - // In case this stream is scheduled for sending data, remove it - // from the schedule queue - Unschedule(); - - // If there is data currently buffered in the streambuf_, - // then cancel will call out to invoke an arbitrary - // JavaScript callback (the on write callback). Within - // that callback, however, the QuicStream will no longer - // be usable to send or receive data. - streambuf_.End(); - streambuf_.Cancel(); - CHECK_EQ(streambuf_.length(), 0); -} - -void QuicStream::Schedule(Queue* queue) { - if (!stream_queue_.IsEmpty()) // Already scheduled? - return; - queue->PushBack(this); -} - -void QuicStream::Unschedule() { - stream_queue_.Remove(); -} - -} // namespace quic -} // namespace node - -#endif // NODE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_STREAM_INL_H_ diff --git a/src/quic/node_quic_stream.cc b/src/quic/node_quic_stream.cc deleted file mode 100644 index 57976ae50d9..00000000000 --- a/src/quic/node_quic_stream.cc +++ /dev/null @@ -1,548 +0,0 @@ -#include "node_quic_stream-inl.h" // NOLINT(build/include) -#include "aliased_struct-inl.h" -#include "async_wrap-inl.h" -#include "debug_utils-inl.h" -#include "env-inl.h" -#include "node.h" -#include "node_buffer.h" -#include "node_internals.h" -#include "stream_base-inl.h" -#include "node_sockaddr-inl.h" -#include "node_http_common-inl.h" -#include "node_quic_session-inl.h" -#include "node_quic_socket-inl.h" -#include "node_quic_util-inl.h" -#include "v8.h" -#include "uv.h" - -#include <algorithm> -#include <memory> -#include <string> -#include <utility> - -namespace node { - -using v8::Array; -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::FunctionTemplate; -using v8::Local; -using v8::Object; -using v8::ObjectTemplate; -using v8::PropertyAttribute; -using v8::Value; - -namespace quic { - -QuicStream::QuicStream( - QuicSession* sess, - Local<Object> wrap, - int64_t stream_id, - int64_t push_id) - : AsyncWrap(sess->env(), wrap, AsyncWrap::PROVIDER_QUICSTREAM), - StreamBase(sess->env()), - StatsBase(sess->env(), wrap, - HistogramOptions::ACK | - HistogramOptions::RATE | - HistogramOptions::SIZE), - session_(sess), - stream_id_(stream_id), - push_id_(push_id), - state_(sess->env()->isolate()), - quic_state_(sess->quic_state()) { - CHECK_NOT_NULL(sess); - Debug(this, "Created"); - StreamBase::AttachToObject(GetObject()); - - wrap->DefineOwnProperty( - env()->context(), - env()->state_string(), - state_.GetArrayBuffer(), - PropertyAttribute::ReadOnly).Check(); - - ngtcp2_transport_params params; - ngtcp2_conn_get_local_transport_params(session()->connection(), ¶ms); - IncrementStat(&QuicStreamStats::max_offset, params.initial_max_data); -} - -QuicStream::~QuicStream() { - DebugStats(); -} - -template <typename Fn> -void QuicStreamStatsTraits::ToString(const QuicStream& ptr, Fn&& add_field) { -#define V(_n, name, label) \ - add_field(label, ptr.GetStat(&QuicStreamStats::name)); - STREAM_STATS(V) -#undef V -} - -// Acknowledge is called when ngtcp2 has received an acknowledgement -// for one or more stream frames for this QuicStream. This will cause -// data stored in the streambuf_ outbound queue to be consumed and may -// result in the JavaScript callback for the write to be invoked. -void QuicStream::Acknowledge(uint64_t offset, size_t datalen) { - if (is_destroyed()) - return; - - // ngtcp2 guarantees that offset must always be greater - // than the previously received offset, but let's just - // make sure that holds. - CHECK_GE(offset, GetStat(&QuicStreamStats::max_offset_ack)); - SetStat(&QuicStreamStats::max_offset_ack, offset); - - Debug(this, "Acknowledging %d bytes", datalen); - - // Consumes the given number of bytes in the buffer. This may - // have the side-effect of causing the onwrite callback to be - // invoked if a complete chunk of buffered data has been acknowledged. - streambuf_.Consume(datalen); - - RecordAck(&QuicStreamStats::acked_at); -} - -// While not all QUIC applications will support headers, QuicStream -// includes basic, generic support for storing them. -bool QuicStream::AddHeader(std::unique_ptr<QuicHeader> header) { - size_t len = header->length(); - QuicApplication* app = session()->application(); - // We cannot add the header if we've either reached - // * the max number of header pairs or - // * the max number of header bytes - if (headers_.size() == app->max_header_pairs() || - current_headers_length_ + len > app->max_header_length()) { - return false; - } - - current_headers_length_ += header->length(); - Debug(this, "Header - %s", header.get()); - headers_.emplace_back(std::move(header)); - return true; -} - -std::string QuicStream::diagnostic_name() const { - return std::string("QuicStream ") + std::to_string(stream_id_) + - " (" + std::to_string(static_cast<int64_t>(get_async_id())) + - ", " + session_->diagnostic_name() + ")"; -} - -void QuicStream::Destroy(QuicError* error) { - if (destroyed_) - return; - destroyed_ = true; - - if (is_writable() || is_readable()) - session()->ShutdownStream(id(), 0); - - CancelPendingWrites(); - - session_->RemoveStream(stream_id_); -} - -// Do shutdown is called when the JS stream writable side is closed. -// If we're not within an ngtcp2 callback, this will trigger the -// QuicSession to send any pending data. If a final stream frame -// has not already been sent, it will be after this. -int QuicStream::DoShutdown(ShutdownWrap* req_wrap) { - if (is_destroyed()) - return UV_EPIPE; - - // If the fin bit has already been sent, we can return - // immediately because there's nothing else to do. The - // _final callback will be invoked immediately. - if (state_->fin_sent || !is_writable()) { - Debug(this, "Shutdown write immediately"); - return 1; - } - Debug(this, "Deferred shutdown. Waiting for fin sent"); - - CHECK_NULL(shutdown_done_); - CHECK_NOT_NULL(req_wrap); - shutdown_done_ = [=](int status) { - CHECK_NOT_NULL(req_wrap); - shutdown_done_ = nullptr; - req_wrap->Done(status); - }; - - QuicSession::SendSessionScope send_scope(session()); - - Debug(this, "Shutdown writable side"); - RecordTimestamp(&QuicStreamStats::closing_at); - state_->write_ended = 1; - streambuf_.End(); - session()->ResumeStream(stream_id_); - - return 0; -} - -int QuicStream::DoWrite( - WriteWrap* req_wrap, - uv_buf_t* bufs, - size_t nbufs, - uv_stream_t* send_handle) { - CHECK_NULL(send_handle); - CHECK(!streambuf_.is_ended()); - - // A write should not have happened if we've been destroyed or - // the QuicStream is no longer (or was never) writable. - if (is_destroyed() || !is_writable()) { - req_wrap->Done(UV_EPIPE); - return 0; - } - - // Nothing to write. - size_t length = get_length(bufs, nbufs); - if (length == 0) { - req_wrap->Done(0); - return 0; - } - - QuicSession::SendSessionScope send_scope(session()); - - Debug(this, "Queuing %" PRIu64 " bytes of data from %d buffers", - length, nbufs); - IncrementStat(&QuicStreamStats::bytes_sent, static_cast<uint64_t>(length)); - - BaseObjectPtr<AsyncWrap> strong_ref{req_wrap->GetAsyncWrap()}; - // The list of buffers will be appended onto streambuf_ without - // copying. Those will remain in the buffer until the serialized - // stream frames are acknowledged. - // This callback function will be invoked once this - // complete batch of buffers has been acknowledged - // by the peer. This will have the side effect of - // blocking additional pending writes from the - // javascript side, so writing data to the stream - // will be throttled by how quickly the peer is - // able to acknowledge stream packets. This is good - // in the sense of providing back-pressure, but - // also means that writes will be significantly - // less performant unless written in batches. - streambuf_.Push( - bufs, - nbufs, - [req_wrap, strong_ref](int status) { - req_wrap->Done(status); - }); - - // If end() was called on the JS side, the write_ended flag - // will have been set. This allows us to know early if this - // is the final chunk. But this is only only to be triggered - // if end() was called with a final chunk of data to write. - // Otherwise, we have to wait for DoShutdown to be called. - if (state_->write_ended == 1) { - RecordTimestamp(&QuicStreamStats::closing_at); - streambuf_.End(); - } - - session()->ResumeStream(stream_id_); - - return 0; -} - -bool QuicStream::IsAlive() { - return !is_destroyed() && !IsClosing(); -} - -bool QuicStream::IsClosing() { - return !is_writable() && !is_readable(); -} - -int QuicStream::ReadStart() { - CHECK(!is_destroyed()); - CHECK(is_readable()); - state_->read_started = 1; - state_->read_paused = 0; - IncrementStat( - &QuicStreamStats::max_offset, - inbound_consumed_data_while_paused_); - session_->ExtendStreamOffset(id(), inbound_consumed_data_while_paused_); - return 0; -} - -int QuicStream::ReadStop() { - CHECK(!is_destroyed()); - CHECK(is_readable()); - state_->read_paused = 1; - return 0; -} - -void QuicStream::IncrementStats(size_t datalen) { - uint64_t len = static_cast<uint64_t>(datalen); - IncrementStat(&QuicStreamStats::bytes_received, len); - RecordRate(&QuicStreamStats::received_at); - RecordSize(len); -} - -void QuicStream::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("buffer", &streambuf_); - StatsBase::StatsMemoryInfo(tracker); - tracker->TrackField("headers", headers_); -} - -BaseObjectPtr<QuicStream> QuicStream::New( - QuicSession* session, - int64_t stream_id, - int64_t push_id) { - Local<Object> obj; - if (!session->env() - ->quicserverstream_instance_template() - ->NewInstance(session->env()->context()).ToLocal(&obj)) { - return {}; - } - BaseObjectPtr<QuicStream> stream = - MakeDetachedBaseObject<QuicStream>( - session, - obj, - stream_id, - push_id); - CHECK(stream); - session->AddStream(stream); - return stream; -} - -// Passes chunks of data on to the JavaScript side as soon as they are -// received but only if we're still readable. The caller of this must have a -// HandleScope. -// -// Note that this is pushing data to the JS side regardless of whether -// anything is listening. For flow-control, we only send window updates -// to the sending peer if the stream is in flowing mode, so the sender -// should not be sending too much data. -void QuicStream::ReceiveData( - uint32_t flags, - const uint8_t* data, - size_t datalen, - uint64_t offset) { - CHECK(!is_destroyed()); - Debug(this, "Receiving %d bytes. Final? %s. Readable? %s", - datalen, - flags & NGTCP2_STREAM_DATA_FLAG_FIN ? "yes" : "no", - is_readable() ? "yes" : "no"); - - // If the QuicStream is not (or was never) readable, just ignore the chunk. - if (!is_readable()) - return; - - // ngtcp2 guarantees that datalen will only be 0 if fin is set. - // Let's just make sure. - CHECK(datalen > 0 || flags & NGTCP2_STREAM_DATA_FLAG_FIN); - - // ngtcp2 guarantees that offset is always greater than the previously - // received offset. Let's just make sure. - CHECK_GE(offset, GetStat(&QuicStreamStats::max_offset_received)); - SetStat(&QuicStreamStats::max_offset_received, offset); - - if (datalen > 0) { - // IncrementStats will update the data_rx_rate_ and data_rx_size_ - // histograms. These will provide data necessary to detect and - // prevent Slow Send DOS attacks specifically by allowing us to - // see if a connection is sending very small chunks of data at very - // slow speeds. It is important to emphasize, however, that slow send - // rates may be perfectly legitimate so we cannot simply take blanket - // action when slow rates are detected. Nor can we reliably define what - // a slow rate even is! Will will need to determine some reasonable - // default and allow user code to change the default as well as determine - // what action to take. The current strategy will be to trigger an event - // on the stream when data transfer rates are likely to be considered too - // slow. - IncrementStats(datalen); - - while (datalen > 0) { - uv_buf_t buf = EmitAlloc(datalen); - size_t avail = std::min(static_cast<size_t>(buf.len), datalen); - - // For now, we're allocating and copying. Once we determine if we can - // safely switch to a non-allocated mode like we do with http2 streams, - // we can make this branch more efficient by using the LIKELY - // optimization. The way ngtcp2 currently works, however, we have - // to memcpy here. - if (UNLIKELY(buf.base == nullptr)) - buf.base = reinterpret_cast<char*>(const_cast<uint8_t*>(data)); - else - memcpy(buf.base, data, avail); - data += avail; - datalen -= avail; - // Capture read_paused before EmitRead in case user code callbacks - // alter the state when EmitRead is called. - bool read_paused = state_->read_paused == 1; - EmitRead(avail, buf); - // Reading can be paused while we are processing. If that's - // the case, we still want to acknowledge the current bytes - // so that pausing does not throw off our flow control. - if (read_paused) { - inbound_consumed_data_while_paused_ += avail; - } else { - IncrementStat(&QuicStreamStats::max_offset, avail); - session_->ExtendStreamOffset(id(), avail); - } - } - } - - // When fin != 0, we've received that last chunk of data for this - // stream, indicating that the stream will no longer be readable. - if (flags & NGTCP2_STREAM_DATA_FLAG_FIN) { - set_final_size(offset + datalen); - EmitRead(UV_EOF); - } -} - -int QuicStream::DoPull( - bob::Next<ngtcp2_vec> next, - int options, - ngtcp2_vec* data, - size_t count, - size_t max_count_hint) { - return streambuf_.Pull( - std::move(next), - options, - data, - count, - max_count_hint); -} - -// JavaScript API -namespace { -void QuicStreamGetID(const FunctionCallbackInfo<Value>& args) { - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - args.GetReturnValue().Set(static_cast<double>(stream->id())); -} - -void OpenUnidirectionalStream(const FunctionCallbackInfo<Value>& args) { - CHECK(!args.IsConstructCall()); - CHECK(args[0]->IsObject()); - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As<Object>()); - - int64_t stream_id; - if (!session->OpenUnidirectionalStream(&stream_id)) - return; - - BaseObjectPtr<QuicStream> stream = QuicStream::New(session, stream_id); - args.GetReturnValue().Set(stream->object()); -} - -void OpenBidirectionalStream(const FunctionCallbackInfo<Value>& args) { - CHECK(!args.IsConstructCall()); - CHECK(args[0]->IsObject()); - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As<Object>()); - - int64_t stream_id; - if (!session->OpenBidirectionalStream(&stream_id)) - return; - - BaseObjectPtr<QuicStream> stream = QuicStream::New(session, stream_id); - args.GetReturnValue().Set(stream->object()); -} - -void QuicStreamDestroy(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - QuicError error(env, args[0], args[1], QUIC_ERROR_APPLICATION); - stream->Destroy(&error); -} - -void QuicStreamReset(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - - QuicError error(env, args[0], args[1], QUIC_ERROR_APPLICATION); - - stream->ResetStream( - error.family == QUIC_ERROR_APPLICATION ? - error.code : static_cast<uint64_t>(NGTCP2_NO_ERROR)); -} - -void QuicStreamStopSending(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - - QuicError error(env, args[0], args[1], QUIC_ERROR_APPLICATION); - - stream->StopSending( - error.family == QUIC_ERROR_APPLICATION ? - error.code : static_cast<uint64_t>(NGTCP2_NO_ERROR)); -} - -// Requests transmission of a block of informational headers. Not all -// QUIC Applications will support headers. If headers are not supported, -// This will set the return value to false, otherwise the return value -// is set to true -void QuicStreamSubmitInformation(const FunctionCallbackInfo<Value>& args) { - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - CHECK(args[0]->IsArray()); - args.GetReturnValue().Set(stream->SubmitInformation(args[0].As<Array>())); -} - -// Requests transmission of a block of initial headers. Not all -// QUIC Applications will support headers. If headers are not supported, -// this will set the return value to false, otherwise the return value -// is set to true. For http/3, these may be request or response headers. -void QuicStreamSubmitHeaders(const FunctionCallbackInfo<Value>& args) { - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - CHECK(args[0]->IsArray()); - uint32_t flags = QUICSTREAM_HEADER_FLAGS_NONE; - CHECK(args[1]->Uint32Value(stream->env()->context()).To(&flags)); - args.GetReturnValue().Set(stream->SubmitHeaders(args[0].As<Array>(), flags)); -} - -// Requests transmission of a block of trailing headers. Not all -// QUIC Applications will support headers. If headers are not supported, -// this will set the return value to false, otherwise the return value -// is set to true. -void QuicStreamSubmitTrailers(const FunctionCallbackInfo<Value>& args) { - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - CHECK(args[0]->IsArray()); - args.GetReturnValue().Set(stream->SubmitTrailers(args[0].As<Array>())); -} - -// Requests creation of a push stream. Not all QUIC Applications will -// support push streams. If pushes are not supported, the return value -// will be undefined, otherwise the return value will be the created -// QuicStream representing the push. -void QuicStreamSubmitPush(const FunctionCallbackInfo<Value>& args) { - QuicStream* stream; - ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); - CHECK(args[0]->IsArray()); - BaseObjectPtr<QuicStream> push_stream = - stream->SubmitPush(args[0].As<Array>()); - if (push_stream) - args.GetReturnValue().Set(push_stream->object()); -} - -} // namespace - -void QuicStream::Initialize( - Environment* env, - Local<Object> target, - Local<Context> context) { - Local<FunctionTemplate> stream = FunctionTemplate::New(env->isolate()); - stream->Inherit(AsyncWrap::GetConstructorTemplate(env)); - StreamBase::AddMethods(env, stream); - Local<ObjectTemplate> streamt = stream->InstanceTemplate(); - streamt->SetInternalFieldCount(StreamBase::kInternalFieldCount); - streamt->Set(env->owner_symbol(), Null(env->isolate())); - env->SetProtoMethod(stream, "destroy", QuicStreamDestroy); - env->SetProtoMethod(stream, "resetStream", QuicStreamReset); - env->SetProtoMethod(stream, "stopSending", QuicStreamStopSending); - env->SetProtoMethod(stream, "id", QuicStreamGetID); - env->SetProtoMethod(stream, "submitInformation", QuicStreamSubmitInformation); - env->SetProtoMethod(stream, "submitHeaders", QuicStreamSubmitHeaders); - env->SetProtoMethod(stream, "submitTrailers", QuicStreamSubmitTrailers); - env->SetProtoMethod(stream, "submitPush", QuicStreamSubmitPush); - env->set_quicserverstream_instance_template(streamt); - env->SetConstructorFunction(target, "QuicStream", stream); - - env->SetMethod(target, "openBidirectionalStream", OpenBidirectionalStream); - env->SetMethod(target, "openUnidirectionalStream", OpenUnidirectionalStream); -} - -} // namespace quic -} // namespace node diff --git a/src/quic/node_quic_stream.h b/src/quic/node_quic_stream.h deleted file mode 100644 index 6cbdef2416f..00000000000 --- a/src/quic/node_quic_stream.h +++ /dev/null @@ -1,409 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_STREAM_H_ -#define SRC_QUIC_NODE_QUIC_STREAM_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "memory_tracker.h" -#include "aliased_struct.h" -#include "async_wrap.h" -#include "env.h" -#include "node_http_common.h" -#include "node_quic_state.h" -#include "node_quic_util.h" -#include "stream_base-inl.h" -#include "util-inl.h" -#include "v8.h" - -#include <string> -#include <vector> - -namespace node { -namespace quic { - -class QuicSession; -class QuicStream; -class QuicApplication; - -using QuicHeader = NgHeaderBase<QuicApplication>; - -enum QuicStreamHeaderFlags : uint32_t { - // No flags - QUICSTREAM_HEADER_FLAGS_NONE = 0, - - // Set if the initial headers are considered - // terminal (that is, the stream should be closed - // after transmitting the headers). If headers are - // not supported by the QUIC Application, flag is - // ignored. - QUICSTREAM_HEADER_FLAGS_TERMINAL = 1 -}; - -enum QuicStreamHeadersKind : int { - QUICSTREAM_HEADERS_KIND_NONE = 0, - QUICSTREAM_HEADERS_KIND_INFORMATIONAL, - QUICSTREAM_HEADERS_KIND_INITIAL, - QUICSTREAM_HEADERS_KIND_TRAILING, - QUICSTREAM_HEADERS_KIND_PUSH -}; - -#define STREAM_STATS(V) \ - V(CREATED_AT, created_at, "Created At") \ - V(RECEIVED_AT, received_at, "Last Received At") \ - V(ACKED_AT, acked_at, "Last Acknowledged At") \ - V(CLOSING_AT, closing_at, "Closing At") \ - V(DESTROYED_AT, destroyed_at, "Destroyed At") \ - V(BYTES_RECEIVED, bytes_received, "Bytes Received") \ - V(BYTES_SENT, bytes_sent, "Bytes Sent") \ - V(MAX_OFFSET, max_offset, "Max Offset") \ - V(MAX_OFFSET_ACK, max_offset_ack, "Max Acknowledged Offset") \ - V(MAX_OFFSET_RECV, max_offset_received, "Max Received Offset") \ - V(FINAL_SIZE, final_size, "Final Size") - -#define V(name, _, __) IDX_QUIC_STREAM_STATS_##name, -enum QuicStreamStatsIdx : int { - STREAM_STATS(V) - IDX_QUIC_STREAM_STATS_COUNT -}; -#undef V - -#define V(_, name, __) uint64_t name; -struct QuicStreamStats { - STREAM_STATS(V) -}; -#undef V - -struct QuicStreamStatsTraits { - using Stats = QuicStreamStats; - using Base = QuicStream; - - template <typename Fn> - static void ToString(const Base& ptr, Fn&& add_field); -}; - -#define QUICSTREAM_SHARED_STATE(V) \ - V(WRITE_ENDED, write_ended, uint8_t) \ - V(READ_STARTED, read_started, uint8_t) \ - V(READ_PAUSED, read_paused, uint8_t) \ - V(READ_ENDED, read_ended, uint8_t) \ - V(FIN_SENT, fin_sent, uint8_t) \ - V(FIN_RECEIVED, fin_received, uint8_t) - -#define V(_, name, type) type name; -struct QuicStreamState { - QUICSTREAM_SHARED_STATE(V); -}; -#undef V - -#define V(id, name, _) \ - IDX_QUICSTREAM_STATE_##id = offsetof(QuicStreamState, name), -enum QuicStreamStateFields { - QUICSTREAM_SHARED_STATE(V) - IDX_QUICSTREAM_STATE_END -}; -#undef V - -enum QuicStreamDirection { - // The QuicStream is readable and writable in both directions - QUIC_STREAM_BIRECTIONAL, - - // The QuicStream is writable and readable in only one direction. - // The direction depends on the QuicStreamOrigin. - QUIC_STREAM_UNIDIRECTIONAL -}; - -enum QuicStreamOrigin { - // The QuicStream was created by the server. - QUIC_STREAM_SERVER, - - // The QuicStream was created by the client. - QUIC_STREAM_CLIENT -}; - -// QuicStream's are simple data flows that, fortunately, do not -// require much. They may be: -// -// * Bidirectional or Unidirectional -// * Server or Client Initiated -// -// The flow direction and origin of the stream are important in -// determining the write and read state (Open or Closed). Specifically: -// -// A Unidirectional stream originating with the Server is: -// -// * Server Writable (Open) but not Client Writable (Closed) -// * Client Readable (Open) but not Server Readable (Closed) -// -// Likewise, a Unidirectional stream originating with the -// Client is: -// -// * Client Writable (Open) but not Server Writable (Closed) -// * Server Readable (Open) but not Client Readable (Closed) -// -// Bidirectional Stream States -// +------------+--------------+--------------------+---------------------+ -// | | Initiated By | Initial Read State | Initial Write State | -// +------------+--------------+--------------------+---------------------+ -// | On Server | Server | Open | Open | -// +------------+--------------+--------------------+---------------------+ -// | On Server | Client | Open | Open | -// +------------+--------------+--------------------+---------------------+ -// | On Client | Server | Open | Open | -// +------------+--------------+--------------------+---------------------+ -// | On Client | Client | Open | Open | -// +------------+--------------+--------------------+---------------------+ -// -// Unidirectional Stream States -// +------------+--------------+--------------------+---------------------+ -// | | Initiated By | Initial Read State | Initial Write State | -// +------------+--------------+--------------------+---------------------+ -// | On Server | Server | Closed | Open | -// +------------+--------------+--------------------+---------------------+ -// | On Server | Client | Open | Closed | -// +------------+--------------+--------------------+---------------------+ -// | On Client | Server | Open | Closed | -// +------------+--------------+--------------------+---------------------+ -// | On Client | Client | Closed | Open | -// +------------+--------------+--------------------+---------------------+ -// -// All data sent via the QuicStream is buffered internally until either -// receipt is acknowledged from the peer or attempts to send are abandoned. -// -// A QuicStream may be in a fully Closed (Read and Write) state but still -// have unacknowledged data in it's outbound queue. -// -// A QuicStream is gracefully closed when (a) both Read and Write states -// are Closed, (b) all queued data has been acknowledged. -// -// The JavaScript Writable side of the QuicStream may be shutdown before -// all pending queued data has been serialized to frames. During this state, -// no additional data may be queued to send. -// -// The Write state of a QuicStream will not be closed while there is still -// pending writes on the JavaScript side. -// -// The QuicStream may be forcefully closed immediately using destroy(err). -// This causes all queued data and pending JavaScript writes to be -// abandoned, and causes the QuicStream to be immediately closed at the -// ngtcp2 level. -class QuicStream : public AsyncWrap, - public bob::SourceImpl<ngtcp2_vec>, - public StreamBase, - public StatsBase<QuicStreamStatsTraits> { - public: - static void Initialize( - Environment* env, - v8::Local<v8::Object> target, - v8::Local<v8::Context> context); - - static BaseObjectPtr<QuicStream> New( - QuicSession* session, - int64_t stream_id, - int64_t push_id = 0); - - QuicStream( - QuicSession* session, - v8::Local<v8::Object> target, - int64_t stream_id, - int64_t push_id = 0); - - ~QuicStream() override; - - std::string diagnostic_name() const override; - - // The numeric identifier of the QuicStream. - int64_t id() const { return stream_id_; } - - // If the QuicStream is associated with a push promise, - // the numeric identifier of the promise. Currently only - // used by HTTP/3. - int64_t push_id() const { return push_id_; } - - QuicSession* session() const { return session_.get(); } - - // A QuicStream can be either uni- or bi-directional. - inline QuicStreamDirection direction() const; - - // A QuicStream can be initiated by either the client - // or the server. - inline QuicStreamOrigin origin() const; - - inline void set_fin_sent(); - - inline bool is_destroyed() const { return destroyed_; } - - inline void set_destroyed(); - - // A QuicStream will not be writable if: - // - The streambuf_ is ended - // - It is a Unidirectional stream originating from the peer - inline bool is_writable() const; - - // A QuicStream will not be readable if: - // - The read ended flag is set or - // - It is a Unidirectional stream originating from the local peer. - inline bool is_readable() const; - - // IsWriteFinished will return true if a final stream frame - // has been sent and all data has been acknowledged (the - // send buffer is empty). - inline bool is_write_finished() const; - - // Specifies the kind of headers currently being processed. - inline void set_headers_kind(QuicStreamHeadersKind kind); - - // Set the final size for the QuicStream. This only works - // the first time it is called. Subsequent calls will be - // ignored unless the subsequent size is greater than the - // prior set size, in which case we have a bug and we'll - // assert. - inline void set_final_size(uint64_t final_size); - - // The final size is the maximum amount of data that has been - // acknowleged to have been received for a QuicStream. - uint64_t final_size() const { - return GetStat(&QuicStreamStats::final_size); - } - - // Marks the given data range as having been acknowledged. - // This means that the data range may be released from - // memory. - void Acknowledge(uint64_t offset, size_t datalen); - - // Destroy the QuicStream and render it no longer usable. - void Destroy(QuicError* error = nullptr); - - inline void CancelPendingWrites(); - - // Buffers chunks of data to be written to the QUIC connection. - int DoWrite( - WriteWrap* req_wrap, - uv_buf_t* bufs, - size_t nbufs, - uv_stream_t* send_handle) override; - - // Returns false if the header cannot be added. This will - // typically only happen if a maximimum number of headers - // has been reached. - bool AddHeader(std::unique_ptr<QuicHeader> header); - - // Some QUIC applications support headers, others do not. - // The following methods allow consistent handling of - // headers at the QuicStream level regardless of the - // protocol. For applications that do not support headers, - // these are simply not used. - inline void BeginHeaders( - QuicStreamHeadersKind kind = QUICSTREAM_HEADERS_KIND_NONE); - - // Indicates an amount of unacknowledged data that has been - // submitted to the QUIC connection. - inline void Commit(size_t amount); - - inline void EndHeaders(int64_t push_id = 0); - - // Passes a chunk of data on to the QuicStream listener. - void ReceiveData( - uint32_t flags, - const uint8_t* data, - size_t datalen, - uint64_t offset); - - // Resets the QUIC stream, sending a signal to the peer that - // no additional data will be transmitted for this stream. - inline void ResetStream(uint64_t app_error_code = NGTCP2_NO_ERROR); - - inline void StopSending(uint64_t app_error_code = NGTCP2_NO_ERROR); - - // Submits informational headers. Returns false if headers are not - // supported on the underlying QuicApplication. - inline bool SubmitInformation(v8::Local<v8::Array> headers); - - // Submits initial headers. Returns false if headers are not - // supported on the underlying QuicApplication. - inline bool SubmitHeaders(v8::Local<v8::Array> headers, uint32_t flags); - - // Submits trailing headers. Returns false if headers are not - // supported on the underlying QuicApplication. - inline bool SubmitTrailers(v8::Local<v8::Array> headers); - - inline BaseObjectPtr<QuicStream> SubmitPush(v8::Local<v8::Array> headers); - - // Required for StreamBase - bool IsAlive() override; - - // Required for StreamBase - bool IsClosing() override; - - // Required for StreamBase - int ReadStart() override; - - // Required for StreamBase - int ReadStop() override; - - // Required for StreamBase - int DoShutdown(ShutdownWrap* req_wrap) override; - - AsyncWrap* GetAsyncWrap() override { return this; } - - QuicState* quic_state() { return quic_state_.get(); } - - // Required for MemoryRetainer - void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(QuicStream) - SET_SELF_SIZE(QuicStream) - - protected: - int DoPull( - bob::Next<ngtcp2_vec> next, - int options, - ngtcp2_vec* data, - size_t count, - size_t max_count_hint) override; - - private: - // WasEverWritable returns true if it is a bidirectional stream, - // or a Unidirectional stream originating from the local peer. - // If was_ever_writable() is false, then no stream frames should - // ever be sent from the local peer, including final stream frames. - inline bool was_ever_writable() const; - - // WasEverReadable returns true if it is a bidirectional stream, - // or a Unidirectional stream originating from the remote - // peer. - inline bool was_ever_readable() const; - - void IncrementStats(size_t datalen); - - BaseObjectWeakPtr<QuicSession> session_; - QuicBuffer streambuf_; - - int64_t stream_id_ = 0; - int64_t push_id_ = 0; - bool destroyed_ = false; - AliasedStruct<QuicStreamState> state_; - DoneCB shutdown_done_ = nullptr; - - size_t inbound_consumed_data_while_paused_ = 0; - - std::vector<std::unique_ptr<QuicHeader>> headers_; - QuicStreamHeadersKind headers_kind_; - size_t current_headers_length_ = 0; - - ListNode<QuicStream> stream_queue_; - - BaseObjectPtr<QuicState> quic_state_; - - public: - // Linked List of QuicStream objects - using Queue = ListHead<QuicStream, &QuicStream::stream_queue_>; - - inline void Schedule(Queue* queue); - - inline void Unschedule(); -}; - -} // namespace quic -} // namespace node - -#endif // NODE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_STREAM_H_ diff --git a/src/quic/node_quic_util-inl.h b/src/quic/node_quic_util-inl.h deleted file mode 100644 index 65b2e08823d..00000000000 --- a/src/quic/node_quic_util-inl.h +++ /dev/null @@ -1,435 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_UTIL_INL_H_ -#define SRC_QUIC_NODE_QUIC_UTIL_INL_H_ - -#include "debug_utils-inl.h" -#include "node_internals.h" -#include "node_quic_crypto.h" -#include "node_quic_util.h" -#include "memory_tracker-inl.h" -#include "env-inl.h" -#include "histogram-inl.h" -#include "string_bytes.h" -#include "util-inl.h" -#include "uv.h" - -#include <string> - -namespace node { - -namespace quic { - -QuicPath::QuicPath( - const SocketAddress& local, - const SocketAddress& remote) { - ngtcp2_addr_init( - &this->local, - local.data(), - local.length(), - const_cast<SocketAddress*>(&local)); - ngtcp2_addr_init( - &this->remote, - remote.data(), - remote.length(), - const_cast<SocketAddress*>(&remote)); -} - -size_t QuicCID::Hash::operator()(const QuicCID& token) const { - size_t hash = 0; - for (size_t n = 0; n < token->datalen; n++) { - hash ^= std::hash<uint8_t>{}(token->data[n]) + 0x9e3779b9 + - (hash << 6) + (hash >> 2); - } - return hash; -} - -QuicCID& QuicCID::operator=(const QuicCID& cid) { - if (this == &cid) return *this; - this->~QuicCID(); - return *new(this) QuicCID(std::move(cid)); -} - -bool QuicCID::operator==(const QuicCID& other) const { - return memcmp(cid()->data, other.cid()->data, cid()->datalen) == 0; -} - -bool QuicCID::operator!=(const QuicCID& other) const { - return !(*this == other); -} - -std::string QuicCID::ToString() const { - std::vector<char> dest(ptr_->datalen * 2 + 1); - dest[dest.size() - 1] = '\0'; - size_t written = StringBytes::hex_encode( - reinterpret_cast<const char*>(ptr_->data), - ptr_->datalen, - dest.data(), - dest.size()); - return std::string(dest.data(), written); -} - -size_t GetMaxPktLen(const SocketAddress& addr) { - return addr.family() == AF_INET6 ? - NGTCP2_MAX_PKTLEN_IPV6 : - NGTCP2_MAX_PKTLEN_IPV4; -} - -QuicError::QuicError( - int32_t family_, - uint64_t code_) : - family(family_), - code(code_) {} - -QuicError::QuicError( - int32_t family_, - int code_) : - family(family_) { - switch (family) { - case QUIC_ERROR_CRYPTO: - code_ |= NGTCP2_CRYPTO_ERROR; - // Fall-through... - case QUIC_ERROR_SESSION: - code = ngtcp2_err_infer_quic_transport_error_code(code_); - break; - case QUIC_ERROR_APPLICATION: - code = code_; - break; - default: - UNREACHABLE(); - } -} - -QuicError::QuicError(ngtcp2_connection_close_error_code ccec) : - family(QUIC_ERROR_SESSION), - code(ccec.error_code) { - switch (ccec.type) { - case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION: - family = QUIC_ERROR_APPLICATION; - break; - case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT: - if (code & NGTCP2_CRYPTO_ERROR) - family = QUIC_ERROR_CRYPTO; - break; - default: - UNREACHABLE(); - } -} - -QuicError::QuicError( - Environment* env, - v8::Local<v8::Value> codeArg, - v8::Local<v8::Value> familyArg, - int32_t family_) : - family(family_), - code(NGTCP2_NO_ERROR) { - if (codeArg->IsBigInt()) { - code = codeArg.As<v8::BigInt>()->Int64Value(); - } else if (codeArg->IsNumber()) { - double num = 0; - CHECK(codeArg->NumberValue(env->context()).To(&num)); - code = static_cast<uint64_t>(num); - } - if (familyArg->IsNumber()) { - CHECK(familyArg->Int32Value(env->context()).To(&family)); - } -} - -const char* QuicError::family_name() { - switch (family) { - case QUIC_ERROR_SESSION: - return "Session"; - case QUIC_ERROR_APPLICATION: - return "Application"; - case QUIC_ERROR_CRYPTO: - return "Crypto"; - default: - UNREACHABLE(); - } -} - -const ngtcp2_cid* PreferredAddress::cid() const { - return &paddr_->cid; -} - -const uint8_t* PreferredAddress::stateless_reset_token() const { - return paddr_->stateless_reset_token; -} - -std::string PreferredAddress::ipv6_address() const { - char host[NI_MAXHOST]; - // Return an empty string if unable to convert... - if (uv_inet_ntop(AF_INET6, paddr_->ipv6_addr, host, sizeof(host)) != 0) - return std::string(); - - return std::string(host); -} -std::string PreferredAddress::ipv4_address() const { - char host[NI_MAXHOST]; - // Return an empty string if unable to convert... - if (uv_inet_ntop(AF_INET, paddr_->ipv4_addr, host, sizeof(host)) != 0) - return std::string(); - - return std::string(host); -} - -uint16_t PreferredAddress::ipv6_port() const { - return paddr_->ipv6_port; -} - -uint16_t PreferredAddress::ipv4_port() const { - return paddr_->ipv4_port; -} - -bool PreferredAddress::Use(int family) const { - uv_getaddrinfo_t req; - - if (!ResolvePreferredAddress(family, &req)) - return false; - - dest_->addrlen = req.addrinfo->ai_addrlen; - memcpy(dest_->addr, req.addrinfo->ai_addr, req.addrinfo->ai_addrlen); - uv_freeaddrinfo(req.addrinfo); - return true; -} - -bool PreferredAddress::ResolvePreferredAddress( - int local_address_family, - uv_getaddrinfo_t* req) const { - int af; - const uint8_t* binaddr; - uint16_t port; - switch (local_address_family) { - case AF_INET: - if (paddr_->ipv4_port > 0) { - af = AF_INET; - binaddr = paddr_->ipv4_addr; - port = paddr_->ipv4_port; - break; - } - return false; - case AF_INET6: - if (paddr_->ipv6_port > 0) { - af = AF_INET6; - binaddr = paddr_->ipv6_addr; - port = paddr_->ipv6_port; - break; - } - return false; - default: - UNREACHABLE(); - } - - char host[NI_MAXHOST]; - if (uv_inet_ntop(af, binaddr, host, sizeof(host)) != 0) - return false; - - addrinfo hints{}; - hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; - hints.ai_family = af; - hints.ai_socktype = SOCK_DGRAM; - - // Unfortunately ngtcp2 requires the selection of the - // preferred address to be synchronous, which means we - // have to do a sync resolve using uv_getaddrinfo here. - return - uv_getaddrinfo( - env_->event_loop(), - req, - nullptr, - host, - std::to_string(port).c_str(), - &hints) == 0 && - req->addrinfo != nullptr; -} - -StatelessResetToken::StatelessResetToken( - uint8_t* token, - const uint8_t* secret, - const QuicCID& cid) { - GenerateResetToken(token, secret, cid); - memcpy(buf_, token, sizeof(buf_)); -} - -StatelessResetToken::StatelessResetToken( - const uint8_t* secret, - const QuicCID& cid) { - GenerateResetToken(buf_, secret, cid); -} - -StatelessResetToken::StatelessResetToken( - const uint8_t* token) { - memcpy(buf_, token, sizeof(buf_)); -} - -std::string StatelessResetToken::ToString() const { - std::vector<char> dest(NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1); - dest[dest.size() - 1] = '\0'; - size_t written = StringBytes::hex_encode( - reinterpret_cast<const char*>(buf_), - NGTCP2_STATELESS_RESET_TOKENLEN, - dest.data(), - dest.size()); - return std::string(dest.data(), written); -} - -size_t StatelessResetToken::Hash::operator()( - const StatelessResetToken& token) const { - size_t hash = 0; - for (size_t n = 0; n < NGTCP2_STATELESS_RESET_TOKENLEN; n++) - hash ^= std::hash<uint8_t>{}(token.buf_[n]) + 0x9e3779b9 + - (hash << 6) + (hash >> 2); - return hash; -} - -bool StatelessResetToken::operator==(const StatelessResetToken& other) const { - return memcmp(data(), other.data(), NGTCP2_STATELESS_RESET_TOKENLEN) == 0; -} - -bool StatelessResetToken::operator!=(const StatelessResetToken& other) const { - return !(*this == other); -} - -template <typename T> -StatsBase<T>::StatsBase( - Environment* env, - v8::Local<v8::Object> wrap, - int options) { - static constexpr uint64_t kMax = std::numeric_limits<int64_t>::max(); - - // Create the backing store for the statistics - size_t size = sizeof(Stats); - size_t count = size / sizeof(uint64_t); - stats_store_ = v8::ArrayBuffer::NewBackingStore(env->isolate(), size); - stats_ = new (stats_store_->Data()) Stats; - - DCHECK_NOT_NULL(stats_); - stats_->created_at = uv_hrtime(); - - // The stats buffer is exposed as a BigUint64Array on - // the JavaScript side to allow statistics to be monitored. - v8::Local<v8::ArrayBuffer> stats_buffer = - v8::ArrayBuffer::New(env->isolate(), stats_store_); - v8::Local<v8::BigUint64Array> stats_array = - v8::BigUint64Array::New(stats_buffer, 0, count); - USE(wrap->DefineOwnProperty( - env->context(), - env->stats_string(), - stats_array, - v8::PropertyAttribute::ReadOnly)); - - if (options & HistogramOptions::ACK) { - ack_ = HistogramBase::New(env, 1, kMax); - wrap->DefineOwnProperty( - env->context(), - env->ack_string(), - ack_->object(), - v8::PropertyAttribute::ReadOnly).Check(); - } - - if (options & HistogramOptions::RATE) { - rate_ = HistogramBase::New(env, 1, kMax); - wrap->DefineOwnProperty( - env->context(), - env->rate_string(), - rate_->object(), - v8::PropertyAttribute::ReadOnly).Check(); - } - - if (options & HistogramOptions::SIZE) { - size_ = HistogramBase::New(env, 1, kMax); - wrap->DefineOwnProperty( - env->context(), - env->size_string(), - size_->object(), - v8::PropertyAttribute::ReadOnly).Check(); - } -} - -template <typename T> -void StatsBase<T>::IncrementStat(uint64_t Stats::*member, uint64_t amount) { - static constexpr uint64_t kMax = std::numeric_limits<uint64_t>::max(); - stats_->*member += std::min(amount, kMax - stats_->*member); -} - -template <typename T> -void StatsBase<T>::SetStat(uint64_t Stats::*member, uint64_t value) { - stats_->*member = value; -} - -template <typename T> -void StatsBase<T>::RecordTimestamp(uint64_t Stats::*member) { - stats_->*member = uv_hrtime(); -} - -template <typename T> -uint64_t StatsBase<T>::GetStat(uint64_t Stats::*member) const { - return stats_->*member; -} - -template <typename T> -inline void StatsBase<T>::RecordRate(uint64_t Stats::*member) { - CHECK(rate_); - uint64_t received_at = GetStat(member); - uint64_t now = uv_hrtime(); - if (received_at > 0) - rate_->Record(now - received_at); - SetStat(member, now); -} - -template <typename T> -inline void StatsBase<T>::RecordSize(uint64_t val) { - CHECK(size_); - size_->Record(val); -} - -template <typename T> -inline void StatsBase<T>::RecordAck(uint64_t Stats::*member) { - CHECK(ack_); - uint64_t acked_at = GetStat(member); - uint64_t now = uv_hrtime(); - if (acked_at > 0) - ack_->Record(now - acked_at); - SetStat(member, now); -} - -template <typename T> -void StatsBase<T>::StatsMemoryInfo(MemoryTracker* tracker) const { - tracker->TrackField("stats_store", stats_store_); - tracker->TrackField("rate_histogram", rate_); - tracker->TrackField("size_histogram", size_); - tracker->TrackField("ack_histogram", ack_); -} - -template <typename T> -void StatsBase<T>::DebugStats() { - StatsDebug stats_debug(static_cast<typename T::Base*>(this)); - Debug(static_cast<typename T::Base*>(this), "Destroyed. %s", stats_debug); -} - -template <typename T> -std::string StatsBase<T>::StatsDebug::ToString() const { - std::string out = "Statistics:\n"; - auto add_field = [&out](const char* name, uint64_t val) { - out += " "; - out += std::string(name); - out += ": "; - out += std::to_string(val); - out += "\n"; - }; - add_field("Duration", uv_hrtime() - ptr->GetStat(&Stats::created_at)); - T::ToString(*ptr, add_field); - return out; -} - -template <typename T> -size_t get_length(const T* vec, size_t count) { - CHECK_NOT_NULL(vec); - size_t len = 0; - for (size_t n = 0; n < count; n++) - len += vec[n].len; - return len; -} - -} // namespace quic -} // namespace node - -#endif // SRC_QUIC_NODE_QUIC_UTIL_INL_H_ diff --git a/src/quic/node_quic_util.h b/src/quic/node_quic_util.h deleted file mode 100644 index b3afcb9483d..00000000000 --- a/src/quic/node_quic_util.h +++ /dev/null @@ -1,394 +0,0 @@ -#ifndef SRC_QUIC_NODE_QUIC_UTIL_H_ -#define SRC_QUIC_NODE_QUIC_UTIL_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "node.h" -#include "node_sockaddr.h" -#include "uv.h" -#include "v8.h" -#include "histogram.h" -#include "memory_tracker.h" - -#include <ngtcp2/ngtcp2.h> -#include <openssl/ssl.h> - -#include <algorithm> -#include <functional> -#include <limits> -#include <string> -#include <unordered_map> - -namespace node { -namespace quic { - -// k-constants are used internally, all-caps constants -// are exposed to javascript as constants (see node_quic.cc) - -constexpr size_t kMaxSizeT = std::numeric_limits<size_t>::max(); -constexpr size_t kMaxValidateAddressLru = 10; -constexpr size_t kMinInitialQuicPktSize = 1200; -constexpr size_t kScidLen = NGTCP2_MAX_CIDLEN; -constexpr size_t kTokenRandLen = 16; -constexpr size_t kTokenSecretLen = 16; - -constexpr uint64_t DEFAULT_ACTIVE_CONNECTION_ID_LIMIT = 2; -constexpr uint64_t DEFAULT_MAX_CONNECTIONS = - std::min<uint64_t>(kMaxSizeT, kMaxSafeJsInteger); -constexpr uint64_t DEFAULT_MAX_CONNECTIONS_PER_HOST = 100; -constexpr uint64_t DEFAULT_MAX_STREAM_DATA_BIDI_LOCAL = 256 * 1024; -constexpr uint64_t DEFAULT_MAX_STREAM_DATA_BIDI_REMOTE = 256 * 1024; -constexpr uint64_t DEFAULT_MAX_STREAM_DATA_UNI = 256 * 1024; -constexpr uint64_t DEFAULT_MAX_DATA = 1 * 1024 * 1024; -constexpr uint64_t DEFAULT_MAX_STATELESS_RESETS_PER_HOST = 10; -constexpr uint64_t DEFAULT_MAX_STREAMS_BIDI = 100; -constexpr uint64_t DEFAULT_MAX_STREAMS_UNI = 3; -constexpr uint64_t DEFAULT_MAX_IDLE_TIMEOUT = 10; -constexpr uint64_t DEFAULT_RETRYTOKEN_EXPIRATION = 10; -constexpr uint64_t MIN_RETRYTOKEN_EXPIRATION = 1; -constexpr uint64_t MAX_RETRYTOKEN_EXPIRATION = 60; -constexpr uint64_t NGTCP2_APP_NOERROR = 0xff00; - -constexpr int ERR_FAILED_TO_CREATE_SESSION = -1; - -// The preferred address policy determines how a client QuicSession -// handles a server-advertised preferred address. As suggested, the -// preferred address is the address the server would prefer the -// client to use for subsequent communication for a QuicSession. -// The client may choose to ignore the preference but really shouldn't -// without good reason. We currently only support two options but -// additional options may be added later. -enum SelectPreferredAddressPolicy : int { - // Ignore the server-provided preferred address - QUIC_PREFERRED_ADDRESS_IGNORE, - - // Use the server-provided preferred address. - // With this policy in effect, when a client - // receives a preferred address from the server, - // the client QuicSession will be automatically - // switched to use the selected address if it - // matches the current local address family. - QUIC_PREFERRED_ADDRESS_USE -}; - -// QUIC error codes generally fall into two distinct namespaces: -// Connection Errors and Application Errors. Connection errors -// are further subdivided into Crypto and non-Crypto. Application -// errors are entirely specific to the QUIC application being -// used. An easy rule of thumb is that Application errors are -// semantically associated with the ALPN identifier negotiated -// for the QuicSession. So, if a connection is closed with -// family: QUIC_ERROR_APPLICATION and code: 123, you have to -// look at the ALPN identifier to determine exactly what it -// means. Connection (Session) and Crypto errors, on the other -// hand, share the same meaning regardless of the ALPN. -enum QuicErrorFamily : int32_t { - QUIC_ERROR_SESSION, - QUIC_ERROR_CRYPTO, - QUIC_ERROR_APPLICATION -}; - - -template <typename T> class StatsBase; - -template <typename T, typename Q> -struct StatsTraits { - using Stats = T; - using Base = Q; - - template <typename Fn> - static void ToString(const Q& ptr, Fn&& add_field); -}; - -// StatsBase is a utility help for classes (like QuicSession) -// that record performance statistics. The template takes a -// single Traits argument (see QuicStreamStatsTraits in -// node_quic_stream.h as an example). When the StatsBase -// is deconstructed, collected statistics are output to -// Debug automatically. -template <typename T> -class StatsBase { - public: - typedef typename T::Stats Stats; - - // A StatsBase instance may have one of three histogram - // instances. One that records rate of data flow, one - // that records size of data chunk, and one that records - // rate of data acknowledgement. These may be used in - // slightly different ways of different StatsBase - // instances or may be turned off entirely. - enum HistogramOptions { - NONE = 0, - RATE = 1, - SIZE = 2, - ACK = 4 - }; - - inline StatsBase( - Environment* env, - v8::Local<v8::Object> wrap, - int options = HistogramOptions::NONE); - - inline ~StatsBase() { if (stats_ != nullptr) stats_->~Stats(); } - - // The StatsDebug utility is used when StatsBase is destroyed - // to output statistical information to Debug. It is designed - // to only incur a performance cost constructing the debug - // output when Debug output is enabled. - struct StatsDebug { - typename T::Base* ptr; - explicit StatsDebug(typename T::Base* ptr_) : ptr(ptr_) {} - std::string ToString() const; - }; - - // Increments the given stat field by the given amount or 1 if - // no amount is specified. - inline void IncrementStat(uint64_t Stats::*member, uint64_t amount = 1); - - // Sets an entirely new value for the given stat field - inline void SetStat(uint64_t Stats::*member, uint64_t value); - - // Sets the given stat field to the current uv_hrtime() - inline void RecordTimestamp(uint64_t Stats::*member); - - // Gets the current value of the given stat field - inline uint64_t GetStat(uint64_t Stats::*member) const; - - // If the rate histogram is used, records the time elapsed - // between now and the timestamp specified by the member - // field. - inline void RecordRate(uint64_t Stats::*member); - - // If the size histogram is used, records the given size. - inline void RecordSize(uint64_t val); - - // If the ack rate histogram is used, records the time - // elapsed between now and the timestamp specified by - // the member field. - inline void RecordAck(uint64_t Stats::*member); - - inline void StatsMemoryInfo(MemoryTracker* tracker) const; - - inline void DebugStats(); - - private: - BaseObjectPtr<HistogramBase> rate_; - BaseObjectPtr<HistogramBase> size_; - BaseObjectPtr<HistogramBase> ack_; - std::shared_ptr<v8::BackingStore> stats_store_; - Stats* stats_ = nullptr; -}; - -// PreferredAddress is a helper class used only when a client QuicSession -// receives an advertised preferred address from a server. The helper provides -// information about the preferred address. The Use() function is used to let -// ngtcp2 know to use the preferred address for the given family. -class PreferredAddress { - public: - PreferredAddress( - Environment* env, - ngtcp2_addr* dest, - const ngtcp2_preferred_addr* paddr) : - env_(env), - dest_(dest), - paddr_(paddr) {} - - // When a preferred address is advertised by a server, the - // advertisement also includes a new CID and (optionally) - // a stateless reset token. If the preferred address is - // selected, then the client QuicSession will make use of - // these new values. Access to the cid and reset token - // are provided via the PreferredAddress class only as a - // convenience. - inline const ngtcp2_cid* cid() const; - - // The stateless reset token associated with the preferred - // address CID - inline const uint8_t* stateless_reset_token() const; - - // A preferred address advertisement may include both an - // IPv4 and IPv6 address. Only one of which will be used. - - inline std::string ipv4_address() const; - - inline uint16_t ipv4_port() const; - - inline std::string ipv6_address() const; - - inline uint16_t ipv6_port() const; - - // Instructs the QuicSession to use the advertised - // preferred address matching the given family. If - // the advertisement does not include a matching - // address, the preferred address is ignored. - inline bool Use(int family = AF_INET) const; - - private: - inline bool ResolvePreferredAddress( - int local_address_family, - uv_getaddrinfo_t* req) const; - - Environment* env_; - mutable ngtcp2_addr* dest_; - const ngtcp2_preferred_addr* paddr_; -}; - -// QuicError is a helper class used to encapsulate basic -// details about a QUIC protocol error. There are three -// basic types of errors (see QuicErrorFamily) -struct QuicError { - int32_t family; - uint64_t code; - inline QuicError( - int32_t family_ = QUIC_ERROR_SESSION, - int code_ = NGTCP2_NO_ERROR); - inline QuicError( - int32_t family_ = QUIC_ERROR_SESSION, - uint64_t code_ = NGTCP2_NO_ERROR); - explicit inline QuicError(ngtcp2_connection_close_error_code code); - inline QuicError( - Environment* env, - v8::Local<v8::Value> codeArg, - v8::Local<v8::Value> familyArg = v8::Local<v8::Object>(), - int32_t family_ = QUIC_ERROR_SESSION); - inline const char* family_name(); -}; - -// Helper function that returns the maximum QUIC packet size for -// the given socket address. -inline size_t GetMaxPktLen(const SocketAddress& addr); - -// QuicPath is a utility class that wraps ngtcp2_path to adapt -// it to work with SocketAddress -struct QuicPath : public ngtcp2_path { - inline QuicPath(const SocketAddress& local, const SocketAddress& remote); -}; - -struct QuicPathStorage : public ngtcp2_path_storage { - QuicPathStorage() { - ngtcp2_path_storage_zero(this); - } -}; - -// Simple wrapper for ngtcp2_cid that handles hex encoding -// CIDs are used to identify QuicSession instances and may -// be between 0 and 20 bytes in length. -class QuicCID : public MemoryRetainer { - public: - // Empty constructor - QuicCID() : ptr_(&cid_) {} - - // Copy constructor - QuicCID(const QuicCID& cid) : QuicCID(cid->data, cid->datalen) {} - - // Copy constructor - explicit QuicCID(const ngtcp2_cid& cid) : QuicCID(cid.data, cid.datalen) {} - - // Wrap constructor - explicit QuicCID(const ngtcp2_cid* cid) : ptr_(cid) {} - - QuicCID(const uint8_t* cid, size_t len) : QuicCID() { - ngtcp2_cid* ptr = this->cid(); - ngtcp2_cid_init(ptr, cid, len); - ptr_ = ptr; - } - - struct Hash { - inline size_t operator()(const QuicCID& cid) const; - }; - - inline bool operator==(const QuicCID& other) const; - inline bool operator!=(const QuicCID& other) const; - inline QuicCID& operator=(const QuicCID& cid); - const ngtcp2_cid& operator*() const { return *ptr_; } - const ngtcp2_cid* operator->() const { return ptr_; } - - inline std::string ToString() const; - - const ngtcp2_cid* cid() const { return ptr_; } - - const uint8_t* data() const { return ptr_->data; } - - operator bool() const { return ptr_->datalen > 0; } - - size_t length() const { return ptr_->datalen; } - - ngtcp2_cid* cid() { - CHECK_EQ(ptr_, &cid_); - return &cid_; - } - - unsigned char* data() { - return reinterpret_cast<unsigned char*>(cid()->data); - } - - void set_length(size_t length) { - cid()->datalen = length; - } - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(QuicCID) - SET_SELF_SIZE(QuicCID) - - template <typename T> - using Map = std::unordered_map<QuicCID, T, QuicCID::Hash>; - - private: - ngtcp2_cid cid_{}; - const ngtcp2_cid* ptr_; -}; - -// A Stateless Reset Token is a mechanism by which a QUIC -// endpoint can discreetly signal to a peer that it has -// lost all state associated with a connection. This -// helper class is used to both store received tokens and -// provide storage when creating new tokens to send. -class StatelessResetToken : public MemoryRetainer { - public: - inline StatelessResetToken( - uint8_t* token, - const uint8_t* secret, - const QuicCID& cid); - - inline StatelessResetToken( - const uint8_t* secret, - const QuicCID& cid); - - explicit inline StatelessResetToken( - const uint8_t* token); - - inline std::string ToString() const; - - const uint8_t* data() const { return buf_; } - - struct Hash { - inline size_t operator()(const StatelessResetToken& token) const; - }; - - inline bool operator==(const StatelessResetToken& other) const; - inline bool operator!=(const StatelessResetToken& other) const; - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(StatelessResetToken) - SET_SELF_SIZE(StatelessResetToken) - - template <typename T> - using Map = - std::unordered_map< - StatelessResetToken, - BaseObjectPtr<T>, - StatelessResetToken::Hash>; - - private: - uint8_t buf_[NGTCP2_STATELESS_RESET_TOKENLEN]{}; -}; - -template <typename T> -inline size_t get_length(const T*, size_t len); - -} // namespace quic -} // namespace node - -#endif // NOE_WANT_INTERNALS - -#endif // SRC_QUIC_NODE_QUIC_UTIL_H_ diff --git a/test/cctest/test_quic_buffer.cc b/test/cctest/test_quic_buffer.cc deleted file mode 100644 index 0aa18216334..00000000000 --- a/test/cctest/test_quic_buffer.cc +++ /dev/null @@ -1,206 +0,0 @@ -#include "quic/node_quic_buffer-inl.h" -#include "node_bob-inl.h" -#include "util-inl.h" -#include "uv.h" - -#include "gtest/gtest.h" -#include <memory> -#include <vector> - -using node::quic::QuicBuffer; -using node::quic::QuicBufferChunk; -using node::bob::Status; -using node::bob::Options; -using node::bob::Done; -using ::testing::AssertionSuccess; -using ::testing::AssertionFailure; - -::testing::AssertionResult IsEqual(size_t actual, int expected) { - return (static_cast<size_t>(expected) == actual) ? AssertionSuccess() : - AssertionFailure() << actual << " is not equal to " << expected; -} - -TEST(QuicBuffer, Simple) { - char data[100]; - memset(&data, 0, node::arraysize(data)); - uv_buf_t buf = uv_buf_init(data, node::arraysize(data)); - - bool done = false; - - QuicBuffer buffer; - buffer.Push(&buf, 1, [&](int status) { - EXPECT_EQ(0, status); - done = true; - }); - - buffer.Consume(100); - ASSERT_TRUE(IsEqual(buffer.length(), 0)); - - // We have to move the read head forward in order to consume - buffer.Seek(1); - buffer.Consume(100); - ASSERT_TRUE(done); - ASSERT_TRUE(IsEqual(buffer.length(), 0)); -} - -TEST(QuicBuffer, ConsumeMore) { - char data[100]; - memset(&data, 0, node::arraysize(data)); - uv_buf_t buf = uv_buf_init(data, node::arraysize(data)); - - bool done = false; - - QuicBuffer buffer; - buffer.Push(&buf, 1, [&](int status) { - EXPECT_EQ(0, status); - done = true; - }); - - buffer.Seek(1); - buffer.Consume(150); // Consume more than what was buffered - ASSERT_TRUE(done); - ASSERT_TRUE(IsEqual(buffer.length(), 0)); -} - -TEST(QuicBuffer, Multiple) { - uv_buf_t bufs[] { - uv_buf_init(const_cast<char*>("abcdefghijklmnopqrstuvwxyz"), 26), - uv_buf_init(const_cast<char*>("zyxwvutsrqponmlkjihgfedcba"), 26) - }; - - QuicBuffer buf; - bool done = false; - buf.Push(bufs, 2, [&](int status) { done = true; }); - - buf.Seek(2); - ASSERT_TRUE(IsEqual(buf.remaining(), 50)); - ASSERT_TRUE(IsEqual(buf.length(), 52)); - - buf.Consume(25); - ASSERT_TRUE(IsEqual(buf.length(), 27)); - - buf.Consume(25); - ASSERT_TRUE(IsEqual(buf.length(), 2)); - - buf.Consume(2); - ASSERT_TRUE(IsEqual(buf.length(), 0)); -} - -TEST(QuicBuffer, Multiple2) { - char* ptr = new char[100]; - memset(ptr, 0, 50); - memset(ptr + 50, 1, 50); - - uv_buf_t bufs[] = { - uv_buf_init(ptr, 50), - uv_buf_init(ptr + 50, 50) - }; - - int count = 0; - - QuicBuffer buffer; - buffer.Push( - bufs, node::arraysize(bufs), - [&](int status) { - count++; - ASSERT_EQ(0, status); - delete[] ptr; - }); - buffer.Seek(node::arraysize(bufs)); - - buffer.Consume(25); - ASSERT_TRUE(IsEqual(buffer.length(), 75)); - buffer.Consume(25); - ASSERT_TRUE(IsEqual(buffer.length(), 50)); - buffer.Consume(25); - ASSERT_TRUE(IsEqual(buffer.length(), 25)); - buffer.Consume(25); - ASSERT_TRUE(IsEqual(buffer.length(), 0)); - - // The callback was only called once tho - ASSERT_EQ(1, count); -} - -TEST(QuicBuffer, Cancel) { - char* ptr = new char[100]; - memset(ptr, 0, 50); - memset(ptr + 50, 1, 50); - - uv_buf_t bufs[] = { - uv_buf_init(ptr, 50), - uv_buf_init(ptr + 50, 50) - }; - - int count = 0; - - QuicBuffer buffer; - buffer.Push( - bufs, node::arraysize(bufs), - [&](int status) { - count++; - ASSERT_EQ(UV_ECANCELED, status); - delete[] ptr; - }); - - buffer.Seek(1); - buffer.Consume(25); - ASSERT_TRUE(IsEqual(buffer.length(), 75)); - buffer.Cancel(); - ASSERT_TRUE(IsEqual(buffer.length(), 0)); - - // The callback was only called once tho - ASSERT_EQ(1, count); -} - -TEST(QuicBuffer, Move) { - QuicBuffer buffer1; - QuicBuffer buffer2; - - char data[100]; - memset(&data, 0, node::arraysize(data)); - uv_buf_t buf = uv_buf_init(data, node::arraysize(data)); - - buffer1.Push(&buf, 1); - - ASSERT_TRUE(IsEqual(buffer1.length(), 100)); - - buffer2 = std::move(buffer1); - ASSERT_TRUE(IsEqual(buffer1.length(), 0)); - ASSERT_TRUE(IsEqual(buffer2.length(), 100)); -} - -TEST(QuicBuffer, QuicBufferChunk) { - std::unique_ptr<QuicBufferChunk> chunk = - std::make_unique<QuicBufferChunk>(100); - memset(chunk->out(), 1, 100); - - QuicBuffer buffer; - buffer.Push(std::move(chunk)); - buffer.End(); - ASSERT_TRUE(IsEqual(buffer.length(), 100)); - - auto next = [&]( - int status, - const ngtcp2_vec* data, - size_t count, - Done done) { - ASSERT_EQ(status, Status::STATUS_END); - ASSERT_TRUE(IsEqual(count, 1)); - ASSERT_NE(data, nullptr); - done(100); - }; - - ASSERT_TRUE(IsEqual(buffer.remaining(), 100)); - - ngtcp2_vec data[2]; - size_t len = sizeof(data) / sizeof(ngtcp2_vec); - buffer.Pull(next, Options::OPTIONS_SYNC | Options::OPTIONS_END, data, len); - - ASSERT_TRUE(IsEqual(buffer.remaining(), 0)); - - buffer.Consume(50); - ASSERT_TRUE(IsEqual(buffer.length(), 50)); - - buffer.Consume(50); - ASSERT_TRUE(IsEqual(buffer.length(), 0)); -} diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc deleted file mode 100644 index eb1a9c53319..00000000000 --- a/test/cctest/test_quic_cid.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "quic/node_quic_util-inl.h" -#include "node_sockaddr-inl.h" -#include "util-inl.h" -#include "ngtcp2/ngtcp2.h" -#include "gtest/gtest.h" -#include <memory> -#include <vector> - -using node::quic::QuicCID; - -TEST(QuicCID, Simple) { - ngtcp2_cid cid1; - ngtcp2_cid cid2; - uint8_t data1[3] = { 'a', 'b', 'c' }; - uint8_t data2[4] = { 1, 2, 3, 4 }; - ngtcp2_cid_init(&cid1, data1, 3); - ngtcp2_cid_init(&cid2, data2, 4); - - QuicCID qcid1(cid1); - CHECK(qcid1); - CHECK_EQ(qcid1.length(), 3); - CHECK_EQ(qcid1.ToString(), "616263"); - - QuicCID qcid2(cid2); - qcid1 = qcid2; - CHECK_EQ(qcid1.ToString(), qcid2.ToString()); - - qcid1.set_length(5); - memset(qcid1.data(), 1, 5); - CHECK_EQ(qcid1.ToString(), "0101010101"); -} diff --git a/test/common/index.js b/test/common/index.js index b8ffc9dbff9..b70972b1a54 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -50,7 +50,6 @@ const noop = () => {}; const hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; -const hasQuic = hasCrypto && Boolean(process.versions.ngtcp2); // Check for flags. Skip this for workers (both, the `cluster` module and // `worker_threads`) and child processes. @@ -727,7 +726,6 @@ const common = { getTTYfd, hasIntl, hasCrypto, - hasQuic, hasMultiLocalhost, invalidArgTypeHelper, isAIX, diff --git a/test/common/quic.js b/test/common/quic.js deleted file mode 100644 index bee54d81717..00000000000 --- a/test/common/quic.js +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-disable node-core/require-common-first, node-core/required-modules */ -'use strict'; - -// Common bits for all QUIC-related tests -const { debuglog } = require('util'); -const { readKeys } = require('./fixtures'); -const { createWriteStream } = require('fs'); -const kHttp3Alpn = 'h3-29'; - -const [ key, cert, ca ] = - readKeys( - 'binary', - 'agent1-key.pem', - 'agent1-cert.pem', - 'ca1-cert.pem'); - -const debug = debuglog('test'); - -const kServerPort = process.env.NODE_DEBUG_KEYLOG ? 5678 : 0; -const kClientPort = process.env.NODE_DEBUG_KEYLOG ? 5679 : 0; - -function setupKeylog(session) { - if (process.env.NODE_DEBUG_KEYLOG) { - const kl = createWriteStream(process.env.NODE_DEBUG_KEYLOG); - session.on('keylog', kl.write.bind(kl)); - } -} - -module.exports = { - key, - cert, - ca, - debug, - kServerPort, - kClientPort, - setupKeylog, - kHttp3Alpn, -}; diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index 78c33fcf1b6..14484293dc4 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -16,11 +16,6 @@ if (common.hasIntl) { expected_keys.push('unicode'); } -if (common.hasQuic) { - expected_keys.push('ngtcp2'); - expected_keys.push('nghttp3'); -} - expected_keys.sort(); const actual_keys = Object.keys(process.versions).sort(); diff --git a/test/parallel/test-quic-binding.js b/test/parallel/test-quic-binding.js deleted file mode 100644 index 2044ed43b48..00000000000 --- a/test/parallel/test-quic-binding.js +++ /dev/null @@ -1,56 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Tests the availability and correctness of internalBinding(quic) - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { internalBinding } = require('internal/test/binding'); - -const quic = internalBinding('quic'); -assert(quic); - -assert(quic.constants); - -// Version numbers used to identify IETF drafts are created by -// adding the draft number to 0xff0000, in this case 19 (25). -assert.strictEqual(quic.constants.NGTCP2_PROTO_VER.toString(16), 'ff00001d'); -assert.strictEqual(quic.constants.NGHTTP3_ALPN_H3, '\u0005h3-29'); - -assert.strictEqual(quic.constants.NGTCP2_NO_ERROR, 0); -assert.strictEqual(quic.constants.NGTCP2_INTERNAL_ERROR, 1); -assert.strictEqual(quic.constants.NGTCP2_CONNECTION_REFUSED, 2); -assert.strictEqual(quic.constants.NGTCP2_FLOW_CONTROL_ERROR, 3); -assert.strictEqual(quic.constants.NGTCP2_STREAM_LIMIT_ERROR, 4); -assert.strictEqual(quic.constants.NGTCP2_STREAM_STATE_ERROR, 5); -assert.strictEqual(quic.constants.NGTCP2_FINAL_SIZE_ERROR, 6); -assert.strictEqual(quic.constants.NGTCP2_FRAME_ENCODING_ERROR, 7); -assert.strictEqual(quic.constants.NGTCP2_TRANSPORT_PARAMETER_ERROR, 8); -assert.strictEqual(quic.constants.NGTCP2_CONNECTION_ID_LIMIT_ERROR, 9); -assert.strictEqual(quic.constants.NGTCP2_PROTOCOL_VIOLATION, 0xa); -assert.strictEqual(quic.constants.NGTCP2_INVALID_TOKEN, 0xb); -assert.strictEqual(quic.constants.NGTCP2_APPLICATION_ERROR, 0xc); -assert.strictEqual(quic.constants.NGTCP2_CRYPTO_BUFFER_EXCEEDED, 0xd); -assert.strictEqual(quic.constants.NGTCP2_KEY_UPDATE_ERROR, 0xe); -assert.strictEqual(quic.constants.NGTCP2_CRYPTO_ERROR, 0x100); - -// The following just tests for the presence of things we absolutely need. -// They don't test the functionality of those things. - -assert(quic.sessionConfig instanceof Float64Array); -assert(quic.http3Config instanceof Float64Array); - -assert(quic.QuicSocket); -assert(quic.QuicEndpoint); -assert(quic.QuicStream); - -assert.strictEqual(typeof quic.createClientSession, 'function'); -assert.strictEqual(typeof quic.openBidirectionalStream, 'function'); -assert.strictEqual(typeof quic.openUnidirectionalStream, 'function'); -assert.strictEqual(typeof quic.setCallbacks, 'function'); -assert.strictEqual(typeof quic.initSecureContext, 'function'); -assert.strictEqual(typeof quic.initSecureContextClient, 'function'); -assert.strictEqual(typeof quic.silentCloseSession, 'function'); diff --git a/test/parallel/test-quic-blocklist.js b/test/parallel/test-quic-blocklist.js deleted file mode 100644 index 383ea3bd49e..00000000000 --- a/test/parallel/test-quic-blocklist.js +++ /dev/null @@ -1,52 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); - -if (!common.hasQuic) - common.skip('missing quic'); - -const { createQuicSocket, BlockList } = require('net'); -const assert = require('assert'); - -const { key, cert, ca } = require('../common/quic'); -const { once } = require('events'); - -const idleTimeout = common.platformTimeout(1); -const options = { key, cert, ca, alpn: 'zzz', idleTimeout }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -assert(client.blockList instanceof BlockList); -assert(server.blockList instanceof BlockList); - -client.blockList.addAddress('10.0.0.1'); - -assert(client.blockList.check('10.0.0.1')); - -// Connection fails because the IP address is blocked -assert.rejects(client.connect({ address: '10.0.0.1' }), { - code: 'ERR_OPERATION_FAILED' -}).then(common.mustCall()); - -server.blockList.addAddress('127.0.0.1'); - -(async () => { - server.on('session', common.mustNotCall()); - - await server.listen(); - - const session = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - idleTimeout, - }); - - session.on('secure', common.mustNotCall()); - - await once(session, 'close'); - - await Promise.all([server.close(), client.close()]); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-client-connect-multiple-parallel.js b/test/parallel/test-quic-client-connect-multiple-parallel.js deleted file mode 100644 index 280524df65d..00000000000 --- a/test/parallel/test-quic-client-connect-multiple-parallel.js +++ /dev/null @@ -1,61 +0,0 @@ -// Flags: --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -// Test that .connect() can be called multiple times with different servers. - -const assert = require('assert'); -const { createQuicSocket } = require('net'); - -const { key, cert, ca } = require('../common/quic'); -const Countdown = require('../common/countdown'); - -const options = { key, cert, ca, alpn: 'meow' }; -const kCount = 3; -const servers = []; - -const client = createQuicSocket({ client: options }); -const countdown = new Countdown(kCount, () => { - client.close(); -}); - -async function connect(server, client) { - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('stream', common.mustCall((stream) => { - stream.on('data', common.mustCall( - (chk) => assert.strictEqual(chk.toString(), 'Hi!'))); - stream.on('end', common.mustCall(() => { - server.close(); - req.close(); - countdown.dec(); - })); - })); - - req.on('close', common.mustCall()); -} - -(async function() { - for (let i = 0; i < kCount; i++) { - const server = createQuicSocket({ server: options }); - - server.on('session', common.mustCall(async (session) => { - const stream = await session.openStream({ halfOpen: true }); - stream.end('Hi!'); - })); - - server.on('close', common.mustCall()); - - servers.push(server); - } - - await Promise.all(servers.map((server) => server.listen())); - - await Promise.all(servers.map((server) => connect(server, client))); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-client-connect-multiple-sequential.js b/test/parallel/test-quic-client-connect-multiple-sequential.js deleted file mode 100644 index 5b1150d1cb1..00000000000 --- a/test/parallel/test-quic-client-connect-multiple-sequential.js +++ /dev/null @@ -1,63 +0,0 @@ -// Flags: --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -// Test that .connect() can be called multiple times with different servers. - -const assert = require('assert'); -const { createQuicSocket } = require('net'); - -const { key, cert, ca } = require('../common/quic'); -const Countdown = require('../common/countdown'); -const { once } = require('events'); - -const options = { key, cert, ca, alpn: 'meow' }; -const kCount = 3; -const servers = []; - -const client = createQuicSocket({ client: options }); -const countdown = new Countdown(kCount, () => { - client.close(); -}); - -async function connect(server, client) { - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('stream', common.mustCall((stream) => { - stream.on('data', common.mustCall( - (chk) => assert.strictEqual(chk.toString(), 'Hi!'))); - stream.on('end', common.mustCall(() => { - server.close(); - req.close(); - countdown.dec(); - })); - })); - - await once(req, 'close'); -} - -(async function() { - for (let i = 0; i < kCount; i++) { - const server = createQuicSocket({ server: options }); - - server.on('session', common.mustCall(async (session) => { - const stream = await session.openStream({ halfOpen: true }); - stream.end('Hi!'); - })); - - server.on('close', common.mustCall()); - - servers.push(server); - } - - await Promise.all(servers.map((server) => server.listen())); - - for (let i = 0; i < kCount; i++) - await connect(servers[i], client); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-client-empty-preferred-address.js b/test/parallel/test-quic-client-empty-preferred-address.js deleted file mode 100644 index 597b312ae8c..00000000000 --- a/test/parallel/test-quic-client-empty-preferred-address.js +++ /dev/null @@ -1,52 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// This test ensures that when we don't define `preferredAddress` -// on the server while the `preferredAddressPolicy` on the client -// is `accept`, it works as expected. - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { key, cert, ca } = require('../common/quic'); -const { createQuicSocket } = require('net'); -const { once } = require('events'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -(async () => { - const server = createQuicSocket({ server: options }); - const client = createQuicSocket({ client: options }); - - server.on('session', common.mustCall((serverSession) => { - serverSession.on('stream', common.mustCall(async (stream) => { - stream.on('data', common.mustCall((data) => { - assert.strictEqual(data.toString('utf8'), 'hello'); - })); - - await once(stream, 'end'); - - stream.close(); - client.close(); - server.close(); - })); - })); - - await server.listen(); - - const clientSession = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - preferredAddressPolicy: 'accept', - }); - - const stream = await clientSession.openStream(); - stream.end('hello'); - - await Promise.all([ - once(stream, 'close'), - once(client, 'close'), - once(server, 'close')]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-client-server.js b/test/parallel/test-quic-client-server.js deleted file mode 100644 index c4fec7b4c7a..00000000000 --- a/test/parallel/test-quic-client-server.js +++ /dev/null @@ -1,364 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Tests a simple QUIC client/server round-trip - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { internalBinding } = require('internal/test/binding'); -const { - constants: { - NGTCP2_NO_ERROR, - QUIC_ERROR_APPLICATION, - } -} = internalBinding('quic'); - -const qlog = process.env.NODE_QLOG === '1'; - -const { Buffer } = require('buffer'); -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { - createReadStream, - createWriteStream, - readFileSync -} = require('fs'); -const { pipeline } = require('stream'); -const { - key, - cert, - ca, - debug, -} = require('../common/quic'); - -const filedata = readFileSync(__filename, { encoding: 'utf8' }); - -const { createQuicSocket } = require('net'); - -const kStatelessResetToken = - Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'); - -const unidata = ['I wonder if it worked.', 'test']; -const kServerName = 'agent2'; // Intentionally the wrong servername -const kALPN = 'zzz'; // ALPN can be overriden to whatever we want - -const { - setImmediate: setImmediatePromise -} = require('timers/promises'); - -const ocspHandler = common.mustCall(async function(type, options) { - debug(`QuicClientSession received an OCSP ${type}"`); - switch (type) { - case 'request': - const { - servername, - certificate, - issuer, - } = options; - - assert.strictEqual(servername, kServerName); - - debug('QuicServerSession Certificate: ', certificate); - debug('QuicServerSession Issuer: ', issuer); - - // Handshake will pause until the Promise resolves - await setImmediatePromise(); - - return Buffer.from('hello'); - case 'response': - const { data } = options; - assert.strictEqual(data.toString(), 'hello'); - } -}, 2); - -const clientHelloHandler = common.mustCall( - async (alpn, servername, ciphers) => { - assert.strictEqual(alpn, kALPN); - assert.strictEqual(servername, kServerName); - assert.strictEqual(ciphers.length, 4); - }); - -const options = { key, cert, ca, alpn: kALPN, qlog, ocspHandler }; - -const client = createQuicSocket({ qlog, client: options }); -const server = createQuicSocket({ - qlog, - validateAddress: true, - statelessResetSecret: kStatelessResetToken, - server: options -}); - -const countdown = new Countdown(2, () => { - debug('Countdown expired. Destroying sockets'); - server.close(); - client.close(); -}); - -function onSocketClose() { - debug(`${this.constructor.name} closing. Duration`, this.duration); - debug(' Bound duration:', - this.boundDuration); - debug(' Listen duration:', - this.listenDuration); - debug(' Bytes Sent/Received: %d/%d', - this.bytesSent, - this.bytesReceived); - debug(' Packets Sent/Received: %d/%d', - this.packetsSent, - this.packetsReceived); - debug(' Sessions:', this.serverSessions, this.clientSessions); -} - -server.on('listening', common.mustCall()); -server.on('ready', common.mustCall()); -server.on('close', common.mustCall(onSocketClose.bind(server))); -client.on('endpointClose', common.mustCall()); -client.on('close', common.mustCall(onSocketClose.bind(client))); - -(async function() { - server.on('session', common.mustCall(async (session) => { - if (qlog) session.qlog.pipe(createWriteStream('server.qlog')); - debug('QuicServerSession Created'); - - assert.strictEqual(session.maxStreams.bidi, 100); - assert.strictEqual(session.maxStreams.uni, 3); - - { - const { - address, - family, - port - } = session.remoteAddress; - const endpoint = client.endpoints[0].address; - assert.strictEqual(port, endpoint.port); - assert.strictEqual(family, endpoint.family); - debug(`QuicServerSession Client ${family} address ${address}:${port}`); - } - - session.on('secure', common.mustCall((servername, alpn, cipher) => { - debug('QuicServerSession TLS Handshake Complete'); - debug(' Server name: %s', servername); - debug(' ALPN: %s', alpn); - debug(' Cipher: %s, %s', cipher.name, cipher.version); - assert.strictEqual(session.servername, servername); - assert.strictEqual(servername, kServerName); - assert.strictEqual(session.alpnProtocol, alpn); - assert.strictEqual(session.getPeerCertificate().subject.CN, 'agent1'); - assert(session.authenticated); - assert.strictEqual(session.authenticationError, undefined); - })); - - const uni = await session.openStream({ halfOpen: true }); - debug('Unidirectional, Server-initiated stream %d opened', uni.id); - assert(uni.writable); - assert(!uni.readable); - assert(uni.unidirectional); - assert(!uni.bidirectional); - assert(uni.serverInitiated); - assert(!uni.clientInitiated); - uni.on('end', common.mustNotCall()); - uni.on('data', common.mustNotCall()); - uni.write(unidata[0], common.mustCall()); - uni.end(unidata[1]); - // TODO(@jasnell): There's currently a bug where the final - // write callback is not invoked if the stream/session is - // destroyed before we receive the acknowledgement for the - // write. - // uni.end(unidata[1], common.mustCall()); - // uni.on('finish', common.mustCall()); - uni.on('close', common.mustCall(() => { - assert.strictEqual(uni.finalSize, 0); - })); - - session.on('stream', common.mustCall((stream) => { - debug('Bidirectional, Client-initiated stream %d received', stream.id); - assert.strictEqual(stream.id, 0); - assert.strictEqual(stream.session, session); - assert(stream.writable); - assert(stream.readable); - assert(stream.bidirectional); - assert(!stream.unidirectional); - assert(stream.clientInitiated); - assert(!stream.serverInitiated); - - let data = ''; - pipeline(createReadStream(__filename), stream, common.mustSucceed()); - - stream.setEncoding('utf8'); - stream.on('blocked', common.mustNotCall()); - stream.on('data', (chunk) => { - data += chunk; - - debug('Server: min data rate: %f', stream.dataRateHistogram.min); - debug('Server: max data rate: %f', stream.dataRateHistogram.max); - debug('Server: data rate 50%: %f', - stream.dataRateHistogram.percentile(50)); - debug('Server: data rate 99%: %f', - stream.dataRateHistogram.percentile(99)); - - debug('Server: min data size: %f', stream.dataSizeHistogram.min); - debug('Server: max data size: %f', stream.dataSizeHistogram.max); - debug('Server: data size 50%: %f', - stream.dataSizeHistogram.percentile(50)); - debug('Server: data size 99%: %f', - stream.dataSizeHistogram.percentile(99)); - }); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, filedata); - debug('Server received expected data for stream %d', stream.id); - })); - stream.on('finish', common.mustCall()); - stream.on('close', common.mustCall(() => { - assert.strictEqual(typeof stream.duration, 'number'); - assert.strictEqual(typeof stream.bytesReceived, 'number'); - assert.strictEqual(typeof stream.bytesSent, 'number'); - assert.strictEqual(typeof stream.maxExtendedOffset, 'number'); - assert.strictEqual(stream.finalSize, filedata.length); - })); - })); - - session.on('close', common.mustCall(() => { - const { - code, - family - } = session.closeCode; - debug(`Server session closed with code ${code} (family: ${family})`); - assert.strictEqual(code, NGTCP2_NO_ERROR); - - const err = { - code: 'ERR_INVALID_STATE', - name: 'Error' - }; - assert.throws(() => session.ping(), err); - assert.throws(() => session.updateKey(), err); - assert.rejects(() => session.openStream(), err); - })); - })); - - await server.listen({ - requestCert: true, - rejectUnauthorized: false, - clientHelloHandler - }); - - const endpoints = server.endpoints; - for (const endpoint of endpoints) { - const address = endpoint.address; - debug('Server is listening on address %s:%d', - address.address, - address.port); - } - const endpoint = endpoints[0]; - - const req = await client.connect({ - address: 'localhost', - port: endpoint.address.port, - servername: kServerName, - }); - if (qlog) req.qlog.pipe(createWriteStream('client.qlog')); - - assert.strictEqual(req.servername, kServerName); - - req.on('usePreferredAddress', common.mustNotCall()); - - req.on('sessionTicket', common.mustCall((ticket, params) => { - debug('Session ticket received'); - assert(ticket instanceof Buffer); - assert(params instanceof Buffer); - debug(' Ticket: %s', ticket.toString('hex')); - debug(' Params: %s', params.toString('hex')); - }, 2)); - - req.on('secure', common.mustCall(async (servername, alpn, cipher) => { - debug('QuicClientSession TLS Handshake Complete'); - debug(' Server name: %s', servername); - debug(' ALPN: %s', alpn); - debug(' Cipher: %s, %s', cipher.name, cipher.version); - assert.strictEqual(servername, kServerName); - assert.strictEqual(req.servername, kServerName); - assert.strictEqual(alpn, kALPN); - assert.strictEqual(req.alpnProtocol, kALPN); - assert(req.ephemeralKeyInfo); - assert.strictEqual(req.getPeerCertificate().subject.CN, 'agent1'); - - debug('Client, min handshake ack: %f', - req.handshakeAckHistogram.min); - debug('Client, max handshake ack: %f', - req.handshakeAckHistogram.max); - debug('Client, min handshake rate: %f', - req.handshakeContinuationHistogram.min); - debug('Client, max handshake rate: %f', - req.handshakeContinuationHistogram.max); - - // The server's identity won't be valid because the requested - // SNI hostname does not match the certificate used. - debug('QuicClientSession server is %sauthenticated', - req.authenticated ? '' : 'not '); - assert(!req.authenticated); - assert.throws(() => { throw req.authenticationError; }, { - code: 'ERR_QUIC_VERIFY_HOSTNAME_MISMATCH', - message: 'Hostname mismatch' - }); - })); - - req.on('stream', common.mustCall((stream) => { - debug('Unidirectional, Server-initiated stream %d received', stream.id); - let data = ''; - assert(stream.readable); - assert(!stream.writable); - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, unidata.join('')); - debug('Client received expected data for stream %d', stream.id); - })); - stream.on('close', common.mustCall(() => { - debug('Unidirectional, Server-initiated stream %d closed', stream.id); - assert.strictEqual(stream.finalSize, 26); - countdown.dec(); - })); - })); - - req.on('close', common.mustCall(() => { - const { - code, - family - } = req.closeCode; - debug(`Client session closed with code ${code} (family: ${family})`); - assert.strictEqual(code, NGTCP2_NO_ERROR); - assert.strictEqual(family, QUIC_ERROR_APPLICATION); - })); - - { - const { - address, - family, - port - } = req.remoteAddress; - const endpoint = server.endpoints[0].address; - assert.strictEqual(port, endpoint.port); - assert.strictEqual(family, endpoint.family); - debug(`QuicClientSession Server ${family} address ${address}:${port}`); - } - - const stream = await req.openStream(); - pipeline(createReadStream(__filename), stream, common.mustSucceed()); - let data = ''; - stream.resume(); - stream.setEncoding('utf8'); - stream.on('finish', common.mustCall()); - stream.on('blocked', common.mustNotCall()); - stream.on('data', (chunk) => data += chunk); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, filedata); - debug('Client received expected data for stream %d', stream.id); - })); - stream.on('close', common.mustCall(() => { - debug('Bidirectional, Client-initiated stream %d closed', stream.id); - assert.strictEqual(stream.finalSize, filedata.length); - countdown.dec(); - })); - debug('Bidirectional, Client-initiated stream %d opened', stream.id); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-errors-quicsession-openstream.js b/test/parallel/test-quic-errors-quicsession-openstream.js deleted file mode 100644 index d608f12d20a..00000000000 --- a/test/parallel/test-quic-errors-quicsession-openstream.js +++ /dev/null @@ -1,86 +0,0 @@ -// Flags: --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -// Test errors thrown when openStream is called incorrectly -// or is not permitted - -const { once } = require('events'); -const { createHook } = require('async_hooks'); -const assert = require('assert'); -const { createQuicSocket } = require('net'); - -// Ensure that no QUICSTREAM instances are created during the test -createHook({ - init(id, type) { - assert.notStrictEqual(type, 'QUICSTREAM'); - } -}).enable(); - -const { key, cert, ca } = require('../common/quic'); - -const options = { key, cert, ca, alpn: 'zzz', maxStreamsUni: 0 }; -const server = createQuicSocket({ server: options }); -const client = createQuicSocket({ client: options }); - -server.on('close', common.mustCall()); -client.on('close', common.mustCall()); - -(async function() { - server.on('session', common.mustCall((session) => { - session.on('stream', common.mustNotCall()); - })); - await server.listen(); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port - }); - - for (const halfOpen of ['z', 1, {}, [], null, Infinity, 1n]) { - await assert.rejects(req.openStream({ halfOpen }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - } - - for (const defaultEncoding of ['', 1n, {}, [], false, 'zebra']) { - await assert.rejects(req.openStream({ defaultEncoding }), { - code: 'ERR_INVALID_ARG_VALUE' - }); - } - - for (const highWaterMark of [-1, Number.MAX_SAFE_INTEGER + 1]) { - await assert.rejects(req.openStream({ highWaterMark }), { - code: 'ERR_OUT_OF_RANGE' - }); - } - - for (const highWaterMark of ['a', 1n, [], {}, false]) { - await assert.rejects(req.openStream({ highWaterMark }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - } - - // Unidirectional streams are not allowed. openStream will succeeed - // but the stream will be destroyed immediately. The underlying - // QuicStream C++ handle will not be created. - await assert.rejects( - req.openStream({ - halfOpen: true, - highWaterMark: 10, - defaultEncoding: 'utf16le' - }), { - code: 'ERR_OPERATION_FAILED' - }); - - server.close(); - client.close(); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-errors-quicsocket-connect.js b/test/parallel/test-quic-errors-quicsocket-connect.js deleted file mode 100644 index e9aba4b0ce5..00000000000 --- a/test/parallel/test-quic-errors-quicsocket-connect.js +++ /dev/null @@ -1,235 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Tests error and input validation checks for QuicSocket.connect() - -const common = require('../common'); - -if (!common.hasQuic) - common.skip('missing quic'); - -const { createHook } = require('async_hooks'); -const assert = require('assert'); -const { createQuicSocket } = require('net'); - -// Ensure that a QuicClientSession handle is never created during the -// error condition tests (ensures that argument and error validation) -// is occurring before the underlying handle is created. -createHook({ - init(id, type) { - assert.notStrictEqual(type, 'QUICCLIENTSESSION'); - } -}).enable(); - -const client = createQuicSocket(); - -(async function() { - await Promise.all(['test', 1n, {}, [], false].map((minDHSize) => { - return assert.rejects(client.connect({ minDHSize }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - await Promise.all([-1, 'test', 1n, {}, [], NaN, false, 65536].map((port) => { - return assert.rejects(client.connect({ port }), { - code: 'ERR_SOCKET_BAD_PORT' - }); - })); - - // Test invalid address argument option - await Promise.all([-1, 10, 1n, {}, [], true].map((address) => { - return assert.rejects(client.connect({ address }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - // Test servername can't be IP address argument option - await Promise.all([ - '0.0.0.0', - '8.8.8.8', - '127.0.0.1', - '192.168.0.1', - '::', - '1::', - '::1', - '1::8', - '1::7:8', - '1:2:3:4:5:6:7:8', - '1:2:3:4:5:6::8', - '2001:0000:1234:0000:0000:C1C0:ABCD:0876', - '3ffe:0b00:0000:0000:0001:0000:0000:000a', - 'a:0:0:0:0:0:0:0', - 'fe80::7:8%eth0', - 'fe80::7:8%1' - ].map((servername) => { - return assert.rejects(client.connect({ servername }), { - code: 'ERR_INVALID_ARG_VALUE' - }); - })); - - await Promise.all([-1, 10, 1n, {}, [], true].map((servername) => { - return assert.rejects(client.connect({ servername }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - // Test invalid remoteTransportParams argument option - await Promise.all([-1, 'test', 1n, {}, []].map((remoteTransportParams) => { - return assert.rejects(client.connect({ remoteTransportParams }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - // Test invalid sessionTicket argument option - await Promise.all([-1, 'test', 1n, {}, []].map((sessionTicket) => { - return assert.rejects(client.connect({ sessionTicket }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - // Test invalid alpn argument option - await Promise.all([-1, 10, 1n, {}, [], true].map((alpn) => { - return assert.rejects(client.connect({ alpn }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - await Promise.all([ - 'idleTimeout', - 'activeConnectionIdLimit', - 'maxAckDelay', - 'maxData', - 'maxUdpPayloadSize', - 'maxStreamDataBidiLocal', - 'maxStreamDataBidiRemote', - 'maxStreamDataUni', - 'maxStreamsBidi', - 'maxStreamsUni', - 'highWaterMark', - ].map(async (prop) => { - await assert.rejects(client.connect({ [prop]: -1 }), { - code: 'ERR_OUT_OF_RANGE' - }); - - await assert.rejects( - client.connect({ [prop]: Number.MAX_SAFE_INTEGER + 1 }), { - code: 'ERR_OUT_OF_RANGE' - }); - - await Promise.all(['a', 1n, [], {}, false].map((val) => { - return assert.rejects(client.connect({ [prop]: val }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - })); - - // activeConnectionIdLimit must be between 2 and 8, inclusive - await Promise.all([1, 9].map((activeConnectionIdLimit) => { - return assert.rejects(client.connect({ activeConnectionIdLimit }), { - code: 'ERR_OUT_OF_RANGE' - }); - })); - - await Promise.all([1, 1n, false, [], {}].map((preferredAddressPolicy) => { - return assert.rejects(client.connect({ preferredAddressPolicy }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - await Promise.all([1, 1n, 'test', [], {}].map((qlog) => { - return assert.rejects(client.connect({ qlog }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - await Promise.all([1, 1n, 'test', [], {}].map((ocspHandler) => { - return assert.rejects(client.connect({ ocspHandler }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - await Promise.all([1, 1n, false, [], {}, 'aaa'].map((type) => { - return assert.rejects(client.connect({ type }), { - code: 'ERR_INVALID_ARG_VALUE' - }); - })); - - await Promise.all([ - 'qpackMaxTableCapacity', - 'qpackBlockedStreams', - 'maxHeaderListSize', - 'maxPushes', - ].map(async (prop) => { - await assert.rejects(client.connect({ h3: { [prop]: -1 } }), { - code: 'ERR_OUT_OF_RANGE' - }); - - await assert.rejects( - client.connect({ h3: { [prop]: Number.MAX_SAFE_INTEGER + 1 } }), { - code: 'ERR_OUT_OF_RANGE' - }); - - await Promise.all(['a', 1n, [], {}, false].map((val) => { - return assert.rejects(client.connect({ h3: { [prop]: val } }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - })); - - await Promise.all(['', 1n, {}, [], false, 'zebra'].map((defaultEncoding) => { - return assert.rejects(client.connect({ defaultEncoding }), { - code: 'ERR_INVALID_ARG_VALUE' - }); - })); - - // Test that connect cannot be called after QuicSocket is closed. - client.close(); - - await assert.rejects(client.connect(), { - code: 'ERR_INVALID_STATE' - }); -})().then(common.mustCall()); - -// TODO(@jasnell): Test additional options: -// -// Client QuicSession Related: -// -// [x] idleTimeout - must be a number greater than zero -// [x] activeConnectionIdLimit - must be a number between 2 and 8 -// [x] maxAckDelay - must be a number greater than zero -// [x] maxData - must be a number greater than zero -// [x] maxUdpPayloadSize - must be a number greater than zero -// [x] maxStreamDataBidiLocal - must be a number greater than zero -// [x] maxStreamDataBidiRemote - must be a number greater than zero -// [x] maxStreamDataUni - must be a number greater than zero -// [x] maxStreamsBidi - must be a number greater than zero -// [x] maxStreamsUni - must be a number greater than zero -// [x] preferredAddressPolicy - must be eiher 'accept' or 'reject' -// [x] qlog - must be a boolean -// [x] requestOCSP - must be a boolean -// [x] type - must be a string, either 'udp4' or 'udp6' -// -// HTTP/3 Related: -// -// [x] h3.qpackMaxTableCapacity - must be a number greater than zero -// [x] h3.qpackBlockedStreams - must be a number greater than zero -// [x] h3.maxHeaderListSize - must be a number greater than zero -// [x] h3.maxPushes - must be a number greater than zero -// -// Secure Context Related: -// -// [ ] ca (certificate authority) - must be a string, string array, -// Buffer, or Buffer array. -// [ ] cert (cert chain) - must be a string, string array, Buffer, or -// Buffer array. -// [ ] ciphers - must be a string -// [ ] clientCertEngine - must be a string -// [ ] crl - must be a string, string array, Buffer, or Buffer array -// [ ] dhparam - must be a string or Buffer -// [ ] ecdhCurve - must be a string -// [ ] honorCipherOrder - must be a boolean -// [ ] key - must be a string, string array, Buffer, or Buffer array -// [ ] passphrase - must be a string -// [ ] pfx - must be a string, string array, Buffer, or Buffer array -// [ ] secureOptions - must be a number -// [x] minDHSize - must be a number diff --git a/test/parallel/test-quic-errors-quicsocket-create.js b/test/parallel/test-quic-errors-quicsocket-create.js deleted file mode 100644 index e7e47339fe3..00000000000 --- a/test/parallel/test-quic-errors-quicsocket-create.js +++ /dev/null @@ -1,166 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Test QuicSocket constructor option errors - -const common = require('../common'); -const async_hooks = require('async_hooks'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); - -// Hook verifies that no QuicSocket handles are actually created. -async_hooks.createHook({ - init: common.mustCallAtLeast((_, type) => { - assert.notStrictEqual(type, 'QUICSOCKET'); - }) -}).enable(); - -const { createQuicSocket } = require('net'); - -// Test invalid QuicSocket options argument -[1, 'test', false, 1n, null].forEach((i) => { - assert.throws(() => createQuicSocket(i), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket port argument option -[-1, 'test', 1n, {}, [], NaN, false].forEach((port) => { - assert.throws(() => createQuicSocket({ endpoint: { port } }), { - code: 'ERR_SOCKET_BAD_PORT' - }); -}); - -// Test invalid QuicSocket addressargument option -[-1, 10, 1n, {}, [], NaN, false].forEach((address) => { - assert.throws(() => createQuicSocket({ endpoint: { address } }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket type argument option -[1, false, 1n, {}, null, NaN].forEach((type) => { - assert.throws(() => createQuicSocket({ endpoint: { type } }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket reuseAddr argument option -[1, NaN, 1n, null, {}, []].forEach((reuseAddr) => { - assert.throws(() => createQuicSocket({ endpoint: { reuseAddr } }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket lookup argument option -[1, 1n, {}, [], 'test', true].forEach((lookup) => { - assert.throws(() => createQuicSocket({ lookup }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket validateAddress argument option -[1, NaN, 1n, null, {}, []].forEach((validateAddress) => { - assert.throws(() => createQuicSocket({ validateAddress }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket qlog argument option -[1, NaN, 1n, null, {}, []].forEach((qlog) => { - assert.throws(() => createQuicSocket({ qlog }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - - -// Test invalid QuicSocket retryTokenTimeout option -[0, 61, NaN].forEach((retryTokenTimeout) => { - assert.throws(() => createQuicSocket({ retryTokenTimeout }), { - code: 'ERR_OUT_OF_RANGE' - }); -}); - -// Test invalid QuicSocket retryTokenTimeout option -['test', null, 1n, {}, [], false].forEach((retryTokenTimeout) => { - assert.throws(() => createQuicSocket({ retryTokenTimeout }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket maxConnectionsPerHost option -[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxConnectionsPerHost) => { - assert.throws(() => createQuicSocket({ maxConnectionsPerHost }), { - code: 'ERR_OUT_OF_RANGE' - }); -}); - -// Test invalid QuicSocket maxConnectionsPerHost option -[ - 'test', - null, - 1n, - {}, - [], - false -].forEach((maxConnectionsPerHost) => { - assert.throws(() => createQuicSocket({ maxConnectionsPerHost }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket maxConnections option -[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxConnections) => { - assert.throws(() => createQuicSocket({ maxConnections }), { - code: 'ERR_OUT_OF_RANGE' - }); -}); - -// Test invalid QuicSocket maxConnectionsPerHost option -[ - 'test', - null, - 1n, - {}, - [], - false -].forEach((maxConnections) => { - assert.throws(() => createQuicSocket({ maxConnections }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -// Test invalid QuicSocket maxStatelessResetsPerHost option -[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxStatelessResetsPerHost) => { - assert.throws(() => createQuicSocket({ maxStatelessResetsPerHost }), { - code: 'ERR_OUT_OF_RANGE' - }); -}); - -// Test invalid QuicSocket maxStatelessResetsPerHost option -[ - 'test', - null, - 1n, - {}, - [], - false -].forEach((maxStatelessResetsPerHost) => { - assert.throws(() => createQuicSocket({ maxStatelessResetsPerHost }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -[1, 1n, false, 'test'].forEach((options) => { - assert.throws(() => createQuicSocket({ endpoint: options }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - assert.throws(() => createQuicSocket({ client: options }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - assert.throws(() => createQuicSocket({ server: options }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); diff --git a/test/parallel/test-quic-errors-quicsocket-listen.js b/test/parallel/test-quic-errors-quicsocket-listen.js deleted file mode 100644 index 19c14fb14fd..00000000000 --- a/test/parallel/test-quic-errors-quicsocket-listen.js +++ /dev/null @@ -1,173 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Tests error and input validation checks for QuicSocket.connect() - -const common = require('../common'); - -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { createQuicSocket } = require('net'); - -async function testAlreadyListening() { - const server = createQuicSocket(); - // Can be called multiple times while pending... - await Promise.all([server.listen(), server.listen()]); - // But fails if called again after resolving - await assert.rejects(server.listen(), { - code: 'ERR_INVALID_STATE' - }); - server.close(); -} - -async function testListenAfterClose() { - const server = createQuicSocket(); - server.close(); - await assert.rejects(server.listen(), { - code: 'ERR_INVALID_STATE' - }); -} - -async function rejectsValue( - server, - name, - values, - code = 'ERR_INVALID_ARG_TYPE') { - for (const v of values) { - await assert.rejects(server.listen({ [name]: v }), { code }); - } -} - -async function testInvalidOptions() { - const server = createQuicSocket(); - - await rejectsValue( - server, - 'alpn', - [1, 1n, true, {}, [], null]); - - for (const prop of [ - 'idleTimeout', - 'activeConnectionIdLimit', - 'maxAckDelay', - 'maxData', - 'maxUdpPayloadSize', - 'maxStreamDataBidiLocal', - 'maxStreamDataBidiRemote', - 'maxStreamDataUni', - 'maxStreamsBidi', - 'maxStreamsUni', - 'highWaterMark', - ]) { - await rejectsValue( - server, - prop, - [-1], - 'ERR_OUT_OF_RANGE'); - await rejectsValue( - server, - prop, - [Number.MAX_SAFE_INTEGER + 1], - 'ERR_OUT_OF_RANGE'); - await rejectsValue( - server, - prop, - ['a', 1n, [], {}, false]); - } - - await rejectsValue( - server, - 'rejectUnauthorized', - [1, 1n, 'test', {}, []]); - - await rejectsValue( - server, - 'requestCert', - [1, 1n, 'test', {}, []]); - - await rejectsValue( - server, - 'ciphers', - [1, 1n, false, {}, [], null]); - - await rejectsValue( - server, - 'groups', - [1, 1n, false, {}, [], null]); - - await rejectsValue( - server, - 'defaultEncoding', - [1, 1n, false, {}, [], 'zebra'], - 'ERR_INVALID_ARG_VALUE'); - - await rejectsValue( - server, - 'preferredAddress', - [1, 1n, 'test', false] - ); - - await assert.rejects( - server.listen({ preferredAddress: { port: -1 } }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - - for (const address of [1, 1n, null, false, [], {}]) { - await assert.rejects( - server.listen({ preferredAddress: { address } }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - } - - for (const type of [1, 'test', false, null, [], {}]) { - await assert.rejects( - server.listen({ preferredAddress: { type } }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - } - - // Make sure that after all of the validation checks, the socket - // is not actually marked as listening at all. - assert.strictEqual(typeof server.listening, 'boolean'); - assert(!server.listening); -} - -(async function() { - await testAlreadyListening(); - await testListenAfterClose(); - await testInvalidOptions(); -})().then(common.mustCall()); - -// Options to check -// * [x] alpn -// * [x] idleTimeout -// * [x] activeConnectionIdLimit -// * [x] maxAckDelay -// * [x] maxData -// * [x] maxUdpPayloadSize -// * [x] maxStreamsBidi -// * [x] maxStreamsUni -// * [x] maxStreamDataBidiLocal -// * [x] maxStreamDataBidiRemote -// * [x] maxStreamDataUni -// * [x] preferredAddress -// * [x] requestCert -// * [x] rejectUnauthorized - -// SecureContext Options -// * [ ] ca -// * [ ] cert -// * [x] ciphers -// * [ ] clientCertEngine -// * [ ] crl -// * [ ] dhparam -// * [ ] groups -// * [ ] ecdhCurve -// * [ ] honorCipherOrder -// * [ ] key -// * [ ] passphrase -// * [ ] pfx -// * [ ] secureOptions -// * [ ] sessionIdContext diff --git a/test/parallel/test-quic-http3-client-server.js b/test/parallel/test-quic-http3-client-server.js deleted file mode 100644 index 2367465947a..00000000000 --- a/test/parallel/test-quic-http3-client-server.js +++ /dev/null @@ -1,151 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Tests a simple QUIC HTTP/3 client/server round-trip - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const fs = require('fs'); - -const { - key, - cert, - ca, - debug, - kHttp3Alpn, - kServerPort, - kClientPort, - setupKeylog, -} = require('../common/quic'); - -const filedata = fs.readFileSync(__filename, { encoding: 'utf8' }); - -const { createQuicSocket } = require('net'); -const kServerName = 'agent2'; // Intentionally the wrong servername - -const options = { key, cert, ca, alpn: kHttp3Alpn }; - -const client = createQuicSocket({ - endpoint: { port: kClientPort }, - client: options -}); -const server = createQuicSocket({ - endpoint: { port: kServerPort }, - server: options -}); - -client.on('close', common.mustCall()); -server.on('close', common.mustCall()); - -const countdown = new Countdown(1, () => { - debug('Countdown expired. Destroying sockets'); - server.close(); - client.close(); -}); - -(async function() { - server.on('session', common.mustCall((session) => { - debug('QuicServerSession Created'); - - assert.strictEqual(session.maxStreams.bidi, 100); - assert.strictEqual(session.maxStreams.uni, 3); - - setupKeylog(session); - - session.on('secure', common.mustCall((_, alpn) => { - debug('QuicServerSession handshake completed'); - assert.strictEqual(session.alpnProtocol, alpn); - })); - - session.on('stream', common.mustCall((stream) => { - debug('Bidirectional, Client-initiated stream %d received', stream.id); - const file = fs.createReadStream(__filename); - let data = ''; - - assert(stream.submitInitialHeaders({ ':status': '200' })); - - file.pipe(stream); - stream.setEncoding('utf8'); - - stream.on('initialHeaders', common.mustCall((headers) => { - const expected = [ - [ ':path', '/' ], - [ ':authority', 'localhost' ], - [ ':scheme', 'https' ], - [ ':method', 'POST' ] - ]; - assert.deepStrictEqual(expected, headers); - debug('Received expected request headers'); - })); - stream.on('informationalHeaders', common.mustNotCall()); - stream.on('trailingHeaders', common.mustNotCall()); - - stream.on('data', (chunk) => { - data += chunk; - }); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, filedata); - debug('Server received expected data for stream %d', stream.id); - })); - stream.on('close', common.mustCall()); - stream.on('finish', common.mustCall()); - })); - - session.on('close', common.mustCall()); - })); - - await server.listen(); - - debug('Server is listening on port %d', server.endpoints[0].address.port); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port, - servername: kServerName, - h3: { maxPushes: 10 } - }); - debug('QuicClientSession Created'); - - req.on('close', common.mustCall()); - - const file = fs.createReadStream(__filename); - const stream = await req.openStream(); - - assert(stream.submitInitialHeaders({ - ':method': 'POST', - ':scheme': 'https', - ':authority': 'localhost', - ':path': '/', - })); - file.pipe(stream); - let data = ''; - stream.resume(); - stream.setEncoding('utf8'); - - stream.on('initialHeaders', common.mustCall((headers) => { - const expected = [ - [ ':status', '200' ] - ]; - assert.deepStrictEqual(expected, headers); - debug('Received expected response headers'); - })); - stream.on('informationalHeaders', common.mustNotCall()); - stream.on('trailingHeaders', common.mustNotCall()); - - stream.on('data', (chunk) => data += chunk); - stream.on('finish', common.mustCall()); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, filedata); - debug('Client received expected data for stream %d', stream.id); - })); - stream.on('close', common.mustCall(() => { - debug('Bidirectional, Client-initiated stream %d closed', stream.id); - countdown.dec(); - })); - debug('Bidirectional, Client-initiated stream %d opened', stream.id); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-http3-push.js b/test/parallel/test-quic-http3-push.js deleted file mode 100644 index 9b5d45f8230..00000000000 --- a/test/parallel/test-quic-http3-push.js +++ /dev/null @@ -1,159 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Tests a simple QUIC HTTP/3 client/server round-trip - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { key, cert, ca, kHttp3Alpn } = require('../common/quic'); - -const { createQuicSocket } = require('net'); -const { createWriteStream } = require('fs'); - -const qlog = process.env.NODE_QLOG === '1'; - -const options = { key, cert, ca, alpn: kHttp3Alpn, qlog }; - -const client = createQuicSocket({ qlog, client: options }); -const server = createQuicSocket({ qlog, server: options }); - -client.on('close', common.mustCall()); -server.on('close', common.mustCall()); - -const countdown = new Countdown(2, () => { - server.close(); - client.close(); -}); - -(async function() { - server.on('session', common.mustCall((session) => { - if (qlog) session.qlog.pipe(createWriteStream('server.qlog')); - session.on('stream', common.mustCall((stream) => { - assert(stream.submitInitialHeaders({ ':status': '200' })); - - [-1, Number.MAX_SAFE_INTEGER + 1].forEach((highWaterMark) => { - assert.throws(() => stream.pushStream({}, { highWaterMark }), { - code: 'ERR_OUT_OF_RANGE' - }); - }); - ['', 1n, {}, [], false].forEach((highWaterMark) => { - assert.throws(() => stream.pushStream({}, { highWaterMark }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - }); - ['', 1, 1n, true, [], {}, 'zebra'].forEach((defaultEncoding) => { - assert.throws(() => stream.pushStream({}, { defaultEncoding }), { - code: 'ERR_INVALID_ARG_VALUE' - }); - }); - - const push = stream.pushStream({ - ':method': 'GET', - ':scheme': 'https', - ':authority': 'localhost', - ':path': '/foo' - }); - assert(push); - push.submitInitialHeaders({ ':status': '200' }); - push.end('testing'); - push.on('close', common.mustCall()); - // TODO(@jasnell): There's current a bug where the last - // _final callback is not being invoked in this case - // push.on('finish', common.mustCall()); - - stream.end('hello world'); - stream.resume(); - stream.on('end', common.mustCall()); - stream.on('close', common.mustCall()); - stream.on('finish', common.mustCall()); - - stream.on('initialHeaders', common.mustCall((headers) => { - const expected = [ - [ ':path', '/' ], - [ ':authority', 'localhost' ], - [ ':scheme', 'https' ], - [ ':method', 'POST' ] - ]; - assert.deepStrictEqual(expected, headers); - })); - stream.on('informationalHeaders', common.mustNotCall()); - stream.on('trailingHeaders', common.mustNotCall()); - })); - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port, - maxStreamsUni: 10, - h3: { maxPushes: 10 } - }); - if (qlog) req.qlog.pipe(createWriteStream('client.qlog')); - - req.on('stream', common.mustCall((stream) => { - let data = ''; - - stream.on('initialHeaders', common.mustCall((headers) => { - const expected = [ - [':status', '200'] - ]; - assert.deepStrictEqual(expected, headers); - })); - - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, 'testing'); - })); - stream.on('close', common.mustCall(() => { - countdown.dec(); - })); - })); - - req.on('close', common.mustCall()); - - const stream = await req.openStream(); - - stream.on('pushHeaders', common.mustCall((headers, push_id) => { - const expected = [ - [ ':path', '/foo' ], - [ ':authority', 'localhost' ], - [ ':scheme', 'https' ], - [ ':method', 'GET' ] - ]; - assert.deepStrictEqual(expected, headers); - assert.strictEqual(push_id, 0); - })); - - stream.on('initialHeaders', common.mustCall((headers) => { - const expected = [ - [ ':status', '200' ] - ]; - assert.deepStrictEqual(expected, headers); - })); - stream.on('informationalHeaders', common.mustNotCall()); - stream.on('trailingHeaders', common.mustNotCall()); - - stream.on('close', common.mustCall(() => { - countdown.dec(); - })); - - assert(stream.submitInitialHeaders({ - ':method': 'POST', - ':scheme': 'https', - ':authority': 'localhost', - ':path': '/', - })); - - stream.end('hello world'); - stream.resume(); - stream.on('finish', common.mustCall()); - stream.on('end', common.mustCall()); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-http3-trailers.js b/test/parallel/test-quic-http3-trailers.js deleted file mode 100644 index 176eacf8aaf..00000000000 --- a/test/parallel/test-quic-http3-trailers.js +++ /dev/null @@ -1,104 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Tests a simple QUIC HTTP/3 client/server round-trip - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { key, cert, ca, kHttp3Alpn } = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: kHttp3Alpn }; -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -client.on('close', common.mustCall()); -server.on('close', common.mustCall()); - -const countdown = new Countdown(1, () => { - server.close(); - client.close(); -}); - -(async function() { - server.on('session', common.mustCall((session) => { - - session.on('stream', common.mustCall((stream) => { - assert(stream.submitInitialHeaders({ ':status': '200' })); - - stream.submitTrailingHeaders({ 'a': 1 }); - stream.end('hello world'); - stream.resume(); - stream.on('end', common.mustCall()); - stream.on('close', common.mustCall()); - stream.on('finish', common.mustCall()); - - stream.on('initialHeaders', common.mustCall((headers) => { - const expected = [ - [ ':path', '/' ], - [ ':authority', 'localhost' ], - [ ':scheme', 'https' ], - [ ':method', 'POST' ] - ]; - assert.deepStrictEqual(expected, headers); - })); - - stream.on('trailingHeaders', common.mustCall((headers) => { - const expected = [ [ 'b', '2' ] ]; - assert.deepStrictEqual(expected, headers); - })); - - stream.on('informationalHeaders', common.mustNotCall()); - })); - - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port, - maxStreamsUni: 10, - h3: { maxPushes: 10 } - }); - - req.on('close', common.mustCall()); - - const stream = await req.openStream(); - - stream.on('trailingHeaders', common.mustCall((headers) => { - const expected = [ [ 'a', '1' ] ]; - assert.deepStrictEqual(expected, headers); - })); - - assert(stream.submitInitialHeaders({ - ':method': 'POST', - ':scheme': 'https', - ':authority': 'localhost', - ':path': '/', - })); - - stream.submitTrailingHeaders({ 'b': 2 }); - stream.end('hello world'); - stream.resume(); - stream.on('finish', common.mustCall()); - stream.on('end', common.mustCall()); - - stream.on('initialHeaders', common.mustCall((headers) => { - const expected = [ - [ ':status', '200' ] - ]; - assert.deepStrictEqual(expected, headers); - })); - stream.on('informationalHeaders', common.mustNotCall()); - - stream.on('close', common.mustCall(() => { - countdown.dec(); - })); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-idle-timeout.js b/test/parallel/test-quic-idle-timeout.js deleted file mode 100644 index a10a8e0d1cf..00000000000 --- a/test/parallel/test-quic-idle-timeout.js +++ /dev/null @@ -1,70 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { createQuicSocket } = require('net'); -const { key, cert, ca } = require('../common/quic'); -const { once } = require('events'); - -const kALPN = 'zzz'; -const idleTimeout = common.platformTimeout(1); -const options = { key, cert, ca, alpn: kALPN }; - -// Test idleTimeout. The test will hang and fail with a timeout -// if the idleTimeout is not working correctly. - -(async () => { - const server = createQuicSocket({ server: options }); - const client = createQuicSocket({ client: options }); - - server.on('session', common.mustCall()); - - await server.listen(); - - const session = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - idleTimeout, - }); - - await once(session, 'close'); - - assert(session.idleTimeout); - client.close(); - server.close(); - - await Promise.all([ - once(client, 'close'), - once(server, 'close') - ]); -})().then(common.mustCall()); - - -(async () => { - const server = createQuicSocket({ server: options }); - const client = createQuicSocket({ client: options }); - - server.on('session', common.mustCall(async (session) => { - await once(session, 'close'); - assert(session.idleTimeout); - client.close(); - server.close(); - await Promise.all([ - once(client, 'close'), - once(server, 'close') - ]); - })); - - await server.listen({ idleTimeout }); - - const session = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - - await once(session, 'close'); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-ipv6only.js b/test/parallel/test-quic-ipv6only.js deleted file mode 100644 index 40b027e431d..00000000000 --- a/test/parallel/test-quic-ipv6only.js +++ /dev/null @@ -1,115 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); - -if (!common.hasIPv6) - common.skip('missing ipv6'); - -if (!common.hasQuic) - common.skip('missing quic'); - -common.skip( - 'temporarily skip ipv6only check. dual stack support ' + - 'is current broken on some platforms'); - -const assert = require('assert'); -const { createQuicSocket } = require('net'); -const { key, cert, ca } = require('../common/quic'); -const { once } = require('events'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -// Connecting to ipv6 server using "127.0.0.1" should work when -// `ipv6Only` is set to `false`. -async function ipv6() { - const server = createQuicSocket({ - endpoint: { type: 'udp6' }, - server: options - }); - const client = createQuicSocket({ client: options }); - - server.on('session', common.mustCall((serverSession) => { - serverSession.on('stream', common.mustCall()); - })); - - await server.listen(); - - const session = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port - }); - - const stream = await session.openStream({ halfOpen: true }); - stream.end('hello'); - - await once(stream, 'close'); - - client.close(); - server.close(); - - await Promise.allSettled([ - once(client, 'close'), - once(server, 'close') - ]); -} - -// When the `ipv6Only` set to `true`, a client cann't connect to it -// through "127.0.0.1". -async function ipv6Only() { - const server = createQuicSocket({ - endpoint: { type: 'udp6-only' }, - server: options - }); - const client = createQuicSocket({ client: options }); - - server.on('session', common.mustNotCall()); - - await server.listen(); - - // This will attempt to connect to the ipv4 localhost address - // but should fail as the connection idle timeout expires. - const session = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - idleTimeout: common.platformTimeout(1), - }); - - session.on('secure', common.mustNotCall()); - - await once(session, 'close'); - - client.close(); - server.close(); - - await Promise.allSettled([ - once(client, 'close'), - once(server, 'close') - ]); -} - -// Creating the QuicSession fails when connect type does not match the -// the connect IP address... -async function mismatch() { - const client = createQuicSocket({ client: options }); - - await assert.rejects(client.connect({ - address: common.localhostIPv4, - port: 1234, - type: 'udp6', - idleTimeout: common.platformTimeout(1), - }), { - code: 'ERR_QUIC_FAILED_TO_CREATE_SESSION' - }); - - client.close(); - - await Promise.allSettled([ - once(client, 'close'), - ]); -} - -ipv6() - .then(ipv6Only) - .then(mismatch) - .then(common.mustCall()); diff --git a/test/parallel/test-quic-keylog.js b/test/parallel/test-quic-keylog.js deleted file mode 100644 index 0c5c7503e13..00000000000 --- a/test/parallel/test-quic-keylog.js +++ /dev/null @@ -1,66 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Tests QUIC keylogging - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { key, cert, ca } = require('../common/quic'); -const { once } = require('events'); - -const { createQuicSocket } = require('net'); - -const kKeylogs = [ - /^CLIENT_HANDSHAKE_TRAFFIC_SECRET .*/, - /^SERVER_HANDSHAKE_TRAFFIC_SECRET .*/, - /^QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET .*/, - /^QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET .*/, - /^CLIENT_TRAFFIC_SECRET_0 .*/, - /^SERVER_TRAFFIC_SECRET_0 .*/, - /^QUIC_CLIENT_TRAFFIC_SECRET_0 .*/, - /^QUIC_SERVER_TRAFFIC_SECRET_0 .*/, -]; - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); -const client = createQuicSocket({ client: options }); - -const kServerKeylogs = Array.from(kKeylogs); -const kClientKeylogs = Array.from(kKeylogs); - -(async () => { - - server.on('session', common.mustCall((session) => { - session.on('keylog', common.mustCall((line) => { - assert.match(line.toString(), kServerKeylogs.shift()); - }, kServerKeylogs.length)); - })); - - await server.listen(); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - - req.on('keylog', common.mustCall((line) => { - assert.match(line.toString(), kClientKeylogs.shift()); - }, kClientKeylogs.length)); - - await once(req, 'secure'); - - server.close(); - client.close(); - - await Promise.allSettled([ - once(server, 'close'), - once(client, 'close') - ]); - - assert.strictEqual(kServerKeylogs.length, 0); - assert.strictEqual(kClientKeylogs.length, 0); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-maxconnectionsperhost.js b/test/parallel/test-quic-maxconnectionsperhost.js deleted file mode 100644 index 91a0a53908f..00000000000 --- a/test/parallel/test-quic-maxconnectionsperhost.js +++ /dev/null @@ -1,73 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { createQuicSocket } = require('net'); -const assert = require('assert'); -const Countdown = require('../common/countdown'); -const { key, cert, ca } = require('../common/quic'); -const options = { key, cert, ca, alpn: 'zzz', idleTimeout: 0 }; - -// QuicSockets must throw errors when maxConnectionsPerHost is not a -// safe integer or is out of range. -{ - [-1, 0, Number.MAX_SAFE_INTEGER + 1, 1.1].forEach((maxConnectionsPerHost) => { - assert.throws(() => createQuicSocket({ maxConnectionsPerHost }), { - code: 'ERR_OUT_OF_RANGE' - }); - }); -} - -// Test that new client sessions will be closed when it exceeds -// maxConnectionsPerHost. -(async function() { - const kMaxConnectionsPerHost = 5; - - const client = createQuicSocket({ client: options }); - const server = createQuicSocket({ - maxConnectionsPerHost: kMaxConnectionsPerHost, - server: options - }); - - const countdown = new Countdown(kMaxConnectionsPerHost + 1, () => { - client.close(); - server.close(); - }); - - server.on('session', common.mustCall(() => {}, kMaxConnectionsPerHost)); - - server.on('close', common.mustCall(() => { - assert.strictEqual(server.serverBusyCount, 1); - })); - - await server.listen(); - - const sessions = []; - for (let i = 0; i < kMaxConnectionsPerHost; i += 1) { - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - req.on('error', common.mustNotCall()); - req.on('close', common.mustCall(() => countdown.dec())); - sessions.push(req); - } - - const extra = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - extra.on('error', common.mustNotCall()); - extra.on('close', common.mustCall(() => { - assert.strictEqual(extra.closeCode.code, 2); - countdown.dec(); - // Shutdown the remaining open sessions. - setImmediate(common.mustCall(() => { - for (const req of sessions) - req.close(); - })); - })); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-process-cleanup.js b/test/parallel/test-quic-process-cleanup.js deleted file mode 100644 index b7ba0caa70d..00000000000 --- a/test/parallel/test-quic-process-cleanup.js +++ /dev/null @@ -1,55 +0,0 @@ -// Flags: --no-warnings --expose-internals -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -// Test that shutting down a process containing an active QUIC server behaves -// well. We use Workers because they have a more clearly defined shutdown -// sequence and we can stop execution at any point. - -const { kRemoveFromSocket } = require('internal/quic/core'); -const { createQuicSocket } = require('net'); -const { Worker, workerData } = require('worker_threads'); - -if (workerData == null) { - new Worker(__filename, { workerData: { removeFromSocket: true } }); - new Worker(__filename, { workerData: { removeFromSocket: false } }); - return; -} - -const { key, cert, ca } = require('../common/quic'); -const options = { key, cert, ca, alpn: 'meow' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); -server.on('close', common.mustNotCall()); -client.on('close', common.mustNotCall()); - -(async function() { - server.on('session', common.mustCall(async (session) => { - const stream = await session.openStream({ halfOpen: false }); - stream.write('Hi!'); - stream.on('data', common.mustNotCall()); - stream.on('finish', common.mustNotCall()); - stream.on('close', common.mustNotCall()); - stream.on('end', common.mustNotCall()); - - session.on('close', common.mustNotCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port - }); - - req.on('stream', common.mustCall(() => { - if (workerData.removeFromSocket) - req[kRemoveFromSocket](); - process.exit(); // Exits the worker thread - })); - - req.on('close', common.mustNotCall()); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-qlog.js b/test/parallel/test-quic-qlog.js deleted file mode 100644 index 3d212346e11..00000000000 --- a/test/parallel/test-quic-qlog.js +++ /dev/null @@ -1,76 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { makeUDPPair } = require('../common/udppair'); -const assert = require('assert'); -const { createQuicSocket } = require('net'); -const { kUDPHandleForTesting } = require('internal/quic/core'); - -const { key, cert, ca } = require('../common/quic'); - -const { serverSide, clientSide } = makeUDPPair(); -const options = { key, cert, ca, alpn: 'meow' }; - -const server = createQuicSocket({ - validateAddress: true, - endpoint: { [kUDPHandleForTesting]: serverSide._handle }, - server: options, - qlog: true -}); -serverSide.afterBind(); - -const client = createQuicSocket({ - endpoint: { [kUDPHandleForTesting]: clientSide._handle }, - client: options, - qlog: true -}); -clientSide.afterBind(); - -(async function() { - server.on('session', common.mustCall(async (session) => { - gatherQlog(session, 'server'); - (await session.openStream({ halfOpen: true })).end('Hi!'); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port, - qlog: true - }); - - gatherQlog(req, 'client'); - - req.on('stream', common.mustCall((stream) => { - stream.resume(); - stream.on('end', common.mustCall(() => { - req.close(); - })); - })); -})().then(common.mustCall()); - -function setupQlog(qlog) { - let data = ''; - qlog.setEncoding('utf8'); - qlog.on('data', (chunk) => data += chunk); - qlog.once('end', common.mustCall(() => { - const { qlog_version, traces } = JSON.parse(data); - assert.strictEqual(typeof qlog_version, 'string'); - assert.strictEqual(typeof traces, 'object'); - })); -} - -function gatherQlog(session, id) { - switch (id) { - case 'server': - setupQlog(session.qlog); - break; - case 'client': - session.on('qlog', setupQlog); - break; - } -} diff --git a/test/parallel/test-quic-quicendpoint-address.js b/test/parallel/test-quic-quicendpoint-address.js deleted file mode 100644 index 742ecfbb71e..00000000000 --- a/test/parallel/test-quic-quicendpoint-address.js +++ /dev/null @@ -1,94 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Tests multiple aspects of QuicSocket multiple endpoint support - -const common = require('../common'); -const { once } = require('events'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); - -const { key, cert, ca } = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -async function Test1(options, address) { - const server = createQuicSocket(options); - server.on('close', common.mustCall()); - - const endpoint = server.endpoints[0]; - - assert.strictEqual(endpoint.bound, false); - assert.deepStrictEqual({}, endpoint.address); - - await endpoint.bind(); - - assert.strictEqual(endpoint.bound, true); - assert.strictEqual(endpoint.destroyed, false); - assert.strictEqual(typeof endpoint.address.port, 'number'); - assert.strictEqual(endpoint.address.address, address); - - await endpoint.close(); - - assert.strictEqual(endpoint.destroyed, true); -} - -async function Test2() { - // Creates a server with multiple endpoints (one on udp4 and udp6) - const server = createQuicSocket({ endpoint: { type: 'udp6' } }); - server.addEndpoint(); - assert.strictEqual(server.endpoints.length, 2); - assert.strictEqual(server.endpoints[0].bound, false); - assert.deepStrictEqual({}, server.endpoints[0].address); - - server.listen({ key, cert, ca, alpn: 'zzz' }); - - // Attempting to add an endpoint after fails. - assert.throws(() => server.addEndpoint(), { - code: 'ERR_INVALID_STATE' - }); - - await once(server, 'ready'); - - assert.strictEqual(server.endpoints.length, 2); - - { - const endpoint = server.endpoints[0]; - assert.strictEqual(endpoint.bound, true); - assert.strictEqual(endpoint.destroyed, false); - assert.strictEqual(endpoint.address.family, 'IPv6'); - assert.strictEqual(typeof endpoint.address.port, 'number'); - assert.strictEqual(endpoint.address.address, '::'); - } - - { - const endpoint = server.endpoints[1]; - assert.strictEqual(endpoint.bound, true); - assert.strictEqual(endpoint.destroyed, false); - assert.strictEqual(endpoint.address.family, 'IPv4'); - assert.strictEqual(typeof endpoint.address.port, 'number'); - assert.strictEqual(endpoint.address.address, '0.0.0.0'); - } - - server.close(); - for (const endpoint of server.endpoints) - assert.strictEqual(endpoint.destroyed, true); -} - -const tests = [ - Test1({}, '0.0.0.0'), - Test1({ endpoint: { port: 0 } }, '0.0.0.0'), - Test1({ endpoint: { address: '127.0.0.1', port: 0 } }, '127.0.0.1'), - Test1({ endpoint: { address: 'localhost', port: 0 } }, '127.0.0.1') -]; - -if (common.hasIPv6) { - tests.push( - Test1({ endpoint: { type: 'udp6' } }, '::'), - Test1({ endpoint: { type: 'udp6', address: 'localhost' } }, '::1'), - Test2()); -} - -Promise.all(tests); diff --git a/test/parallel/test-quic-quicsession-resume.js b/test/parallel/test-quic-quicsession-resume.js deleted file mode 100644 index f8f8ad6d274..00000000000 --- a/test/parallel/test-quic-quicsession-resume.js +++ /dev/null @@ -1,97 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Tests a simple QUIC client/server round-trip - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { Buffer } = require('buffer'); -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { - key, - cert, - ca, - debug, -} = require('../common/quic'); - -const { createWriteStream } = require('fs'); -const { createQuicSocket } = require('net'); - -const qlog = process.env.NODE_QLOG === '1'; -const options = { key, cert, ca, alpn: 'zzz', qlog }; - -const server = createQuicSocket({ qlog, server: options }); -const client = createQuicSocket({ qlog, client: options }); - -const countdown = new Countdown(2, () => { - server.close(); - client.close(); -}); - -(async function() { - let counter = 0; - server.on('session', common.mustCall((session) => { - if (qlog) session.qlog.pipe(createWriteStream(`server-${counter++}.qlog`)); - session.on('stream', common.mustCall((stream) => { - stream.on('close', common.mustCall()); - assert(stream.unidirectional); - assert(!stream.writable); - stream.resume(); - })); - }, 2)); - - await server.listen(); - - let storedTicket; - let storedParams; - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - if (qlog) req.qlog.pipe(createWriteStream(`client-${counter}.qlog`)); - - req.on('sessionTicket', common.mustCall((ticket, params) => { - assert(ticket instanceof Buffer); - assert(params instanceof Buffer); - debug(' Ticket: %s', ticket.toString('hex')); - debug(' Params: %s', params.toString('hex')); - storedTicket = ticket; - storedParams = params; - }, 1)); - - const stream = await req.openStream({ halfOpen: true }); - stream.end('hello'); - stream.on('close', () => { - countdown.dec(); - // Wait a turn then start a new session using the stored - // ticket and transportParameters - setImmediate(newSession, storedTicket, storedParams); - }); - - async function newSession(sessionTicket, remoteTransportParams) { - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - sessionTicket, - remoteTransportParams - }); - if (qlog) req.qlog.pipe(createWriteStream('client2.qlog')); - - assert(req.allowEarlyData); - - const stream = await req.openStream({ halfOpen: true }); - stream.end('hello'); - stream.on('error', common.mustNotCall()); - stream.on('close', common.mustCall(() => countdown.dec())); - - // TODO(@jasnell): This will be false for now because no - // early data was sent. Once we actually start making - // use of early data on the client side, this should be - // true when the early data was accepted. - assert(!req.usingEarlyData); - } -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-send-fd.js b/test/parallel/test-quic-quicsession-send-fd.js deleted file mode 100644 index 6694052d864..00000000000 --- a/test/parallel/test-quic-quicsession-send-fd.js +++ /dev/null @@ -1,99 +0,0 @@ -// Flags: --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { createQuicSocket } = require('net'); -const { once } = require('events'); -const fs = require('fs'); -const qlog = process.env.NODE_QLOG === '1'; - -const { key, cert, ca } = require('../common/quic'); -const options = { key, cert, ca, alpn: 'meow', qlog }; - -const variants = []; -for (const variant of ['sendFD', 'sendFile', 'sendFD+fileHandle']) { - for (const offset of [-1, 0, 100]) { - for (const length of [-1, 100]) { - variants.push({ variant, offset, length }); - } - } -} - -(async function() { - await Promise.all(variants.map(test)); -})().then(common.mustCall()); - -async function test({ variant, offset, length }) { - const server = createQuicSocket({ qlog, server: options }); - const client = createQuicSocket({ qlog, client: options }); - let fd; - - server.on('session', common.mustCall(async (session) => { - if (qlog) { - session.qlog.pipe( - fs.createWriteStream(`server-${variant}-${offset}-${length}.qlog`)); - } - - const stream = await session.openStream({ halfOpen: true }); - - // The data and end events won't emit because - // the stream is never readable. - stream.on('data', common.mustNotCall()); - stream.on('end', common.mustNotCall()); - stream.on('finish', common.mustCall()); - stream.on('close', common.mustCall()); - - if (variant === 'sendFD') { - fd = fs.openSync(__filename, 'r'); - stream.sendFD(fd, { offset, length }); - } else if (variant === 'sendFD+fileHandle') { - fs.promises.open(__filename, 'r').then(common.mustCall((handle) => { - fd = handle; - stream.sendFD(handle, { offset, length }); - })); - } else { - assert.strictEqual(variant, 'sendFile'); - stream.sendFile(__filename, { offset, length }); - } - - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - if (qlog) { - req.qlog.pipe( - fs.createWriteStream(`client-${variant}-${offset}-${length}.qlog`)); - } - - req.on('stream', common.mustCall((stream) => { - const data = []; - stream.on('data', (chunk) => data.push(chunk)); - stream.on('end', common.mustCall(() => { - let expectedContent = fs.readFileSync(__filename); - if (offset !== -1) expectedContent = expectedContent.slice(offset); - if (length !== -1) expectedContent = expectedContent.slice(0, length); - assert.deepStrictEqual(Buffer.concat(data), expectedContent); - if (fd !== undefined) { - if (fd.close) fd.close().then(common.mustCall()); - else fs.closeSync(fd); - } - })); - stream.on('close', common.mustCall(() => { - client.close(); - server.close(); - })); - })); - - await Promise.all([ - once(client, 'close'), - once(server, 'close') - ]); -} diff --git a/test/parallel/test-quic-quicsession-send-file-close-before-open.js b/test/parallel/test-quic-quicsession-send-file-close-before-open.js deleted file mode 100644 index 368a8d046e0..00000000000 --- a/test/parallel/test-quic-quicsession-send-file-close-before-open.js +++ /dev/null @@ -1,53 +0,0 @@ -// Flags: --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { createQuicSocket } = require('net'); -const { once } = require('events'); -const fs = require('fs'); - -const { key, cert, ca } = require('../common/quic'); -const options = { key, cert, ca, alpn: 'meow' }; - -const server = createQuicSocket({ server: options }); -const client = createQuicSocket({ client: options }); - -(async function() { - server.on('session', common.mustCall(async (session) => { - const stream = await session.openStream({ halfOpen: false }); - - fs.open = common.mustCall(fs.open); - fs.close = common.mustCall(fs.close); - - stream.sendFile(__filename); - stream.destroy(); // Destroy the stream before opening the fd finishes. - - session.close(); - - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('stream', common.mustNotCall()); - - req.on('close', common.mustCall(() => { - client.close(); - server.close(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); - -})().then(common.mustCall()); - -server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-send-file-open-error-handled.js b/test/parallel/test-quic-quicsession-send-file-open-error-handled.js deleted file mode 100644 index 96257b9892a..00000000000 --- a/test/parallel/test-quic-quicsession-send-file-open-error-handled.js +++ /dev/null @@ -1,51 +0,0 @@ -// Flags: --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const path = require('path'); -const { createQuicSocket } = require('net'); -const { once } = require('events'); - -const { key, cert, ca } = require('../common/quic'); -const options = { key, cert, ca, alpn: 'meow' }; - -const server = createQuicSocket({ server: options }); -const client = createQuicSocket({ client: options }); - -(async function() { - server.on('session', common.mustCall(async (session) => { - const stream = await session.openStream({ halfOpen: true }); - const nonexistentPath = path.resolve(__dirname, 'nonexistent.file'); - stream.sendFile(nonexistentPath, { - onError: common.expectsError({ - code: 'ENOENT', - syscall: 'open', - path: nonexistentPath - }) - }); - session.close(); - - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('stream', common.mustNotCall()); - - req.on('close', common.mustCall(() => { - client.close(); - server.close(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-send-file-open-error.js b/test/parallel/test-quic-quicsession-send-file-open-error.js deleted file mode 100644 index 5d96c5c82b3..00000000000 --- a/test/parallel/test-quic-quicsession-send-file-open-error.js +++ /dev/null @@ -1,51 +0,0 @@ -// Flags: --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const path = require('path'); -const { createQuicSocket } = require('net'); -const { once } = require('events'); - -const { key, cert, ca } = require('../common/quic'); -const options = { key, cert, ca, alpn: 'meow' }; - -const server = createQuicSocket({ server: options }); -const client = createQuicSocket({ client: options }); - -(async function() { - server.on('session', common.mustCall(async (session) => { - const stream = await session.openStream({ halfOpen: false }); - const nonexistentPath = path.resolve(__dirname, 'nonexistent.file'); - stream.on('error', common.expectsError({ - code: 'ENOENT', - syscall: 'open', - path: nonexistentPath - })); - stream.sendFile(nonexistentPath); - session.close(); - - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('stream', common.mustNotCall()); - - req.on('close', common.mustCall(() => { - server.close(); - client.close(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-server-destroy-early.js b/test/parallel/test-quic-quicsession-server-destroy-early.js deleted file mode 100644 index 011afea8376..00000000000 --- a/test/parallel/test-quic-quicsession-server-destroy-early.js +++ /dev/null @@ -1,54 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Test that destroying a QuicStream immediately and synchronously -// after creation does not crash the process and closes the streams -// abruptly on both ends of the connection. - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { once } = require('events'); -const { key, cert, ca } = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const client = createQuicSocket({ client: options }); - -const server = createQuicSocket({ server: options }); - -(async function() { - server.on('session', common.mustCall((session) => { - session.on('stream', common.mustNotCall()); - session.on('close', common.mustCall(async () => { - await Promise.all([ - client.close(), - server.close() - ]); - assert.rejects(server.close(), { - code: 'ERR_INVALID_STATE', - name: 'Error' - }); - })); - session.destroy(); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - req.on('secure', common.mustNotCall()); - req.on('close', common.mustCall()); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsocket-close.js b/test/parallel/test-quic-quicsocket-close.js deleted file mode 100644 index 03aaf5281f4..00000000000 --- a/test/parallel/test-quic-quicsocket-close.js +++ /dev/null @@ -1,19 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { createQuicSocket } = require('net'); - -const socket = createQuicSocket(); -socket.on('close', common.mustCall()); - -(async function() { - await socket.close(); - assert.rejects(() => socket.close(), { - code: 'ERR_INVALID_STATE' - }); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js b/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js deleted file mode 100644 index d3730423b38..00000000000 --- a/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js +++ /dev/null @@ -1,98 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Tests that stream data is successfully transmitted under -// packet loss conditions on the receiving end. - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -common.skip('temporarily skip packetloss tests for refactoring'); - -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { - key, - cert, - ca, - debug -} = require('../common/quic'); -const { once } = require('events'); -const { pipeline } = require('stream'); - -const { createQuicSocket } = require('net'); - -const kData = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; -const options = { key, cert, ca, alpn: 'echo' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -// Both client and server will drop received packets about 20% of the time -// It is important to keep in mind that this will make the runtime of the -// test non-deterministic. If we encounter flaky timeouts with this test, -// the randomized packet loss will be the reason, but random packet loss -// is exactly what is being tested. So if flaky timeouts do occur, it will -// be best to extend the failure timeout for this test. -server.setDiagnosticPacketLoss({ rx: 0.2 }); -client.setDiagnosticPacketLoss({ rx: 0.2 }); - -const countdown = new Countdown(1, () => { - debug('Countdown expired. Destroying sockets'); - server.close(); - client.close(); -}); - -(async function() { - server.on('session', common.mustCall((session) => { - debug('QuicServerSession Created'); - session.on('stream', common.mustCall((stream) => { - debug('Bidirectional, Client-initiated stream %d received', stream.id); - pipeline(stream, stream, common.mustSucceed()); - })); - })); - - await server.listen(); - - debug('Server is listening on port %d', server.endpoints[0].address.port); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - - const stream = await req.openStream(); - - let n = 0; - // This forces multiple stream packets to be sent out - // rather than all the data being written in a single - // packet. - function sendChunk() { - if (n < kData.length) { - stream.write(kData[n++], common.mustCall()); - setImmediate(sendChunk); - } else { - stream.end(); - } - } - sendChunk(); - - let data = ''; - stream.resume(); - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', common.mustCall(() => { - debug('Received data: %s', kData); - assert.strictEqual(data, kData); - })); - - stream.on('close', common.mustCall(() => { - countdown.dec(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js b/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js deleted file mode 100644 index d33bbf6aef3..00000000000 --- a/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js +++ /dev/null @@ -1,98 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Tests that stream data is successfully transmitted under -// packet loss conditions on the transmitting end. - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -common.skip('temporarily skip packetloss tests for refactoring'); - -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { - key, - cert, - ca, - debug -} = require('../common/quic'); -const { once } = require('events'); -const { pipeline } = require('stream'); - -const { createQuicSocket } = require('net'); - -const kData = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; -const options = { key, cert, ca, alpn: 'echo' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -// Both client and server will drop received packets about 20% of the time -// It is important to keep in mind that this will make the runtime of the -// test non-deterministic. If we encounter flaky timeouts with this test, -// the randomized packet loss will be the reason, but random packet loss -// is exactly what is being tested. So if flaky timeouts do occur, it will -// be best to extend the failure timeout for this test. -server.setDiagnosticPacketLoss({ tx: 0.2 }); -client.setDiagnosticPacketLoss({ tx: 0.2 }); - -const countdown = new Countdown(1, () => { - debug('Countdown expired. Destroying sockets'); - server.close(); - client.close(); -}); - -(async function() { - server.on('session', common.mustCall((session) => { - debug('QuicServerSession Created'); - session.on('stream', common.mustCall((stream) => { - debug('Bidirectional, Client-initiated stream %d received', stream.id); - pipeline(stream, stream, common.mustSucceed()); - })); - })); - - await server.listen(); - - debug('Server is listening on port %d', server.endpoints[0].address.port); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - - const stream = await req.openStream(); - - let n = 0; - // This forces multiple stream packets to be sent out - // rather than all the data being written in a single - // packet. - function sendChunk() { - if (n < kData.length) { - stream.write(kData[n++], common.mustCall()); - setImmediate(sendChunk); - } else { - stream.end(); - } - } - sendChunk(); - - let data = ''; - stream.resume(); - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', common.mustCall(() => { - debug('Received data: %s', kData); - assert.strictEqual(data, kData); - })); - - stream.on('close', common.mustCall(() => { - countdown.dec(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsocket-serverbusy.js b/test/parallel/test-quic-quicsocket-serverbusy.js deleted file mode 100644 index d3fd399344b..00000000000 --- a/test/parallel/test-quic-quicsocket-serverbusy.js +++ /dev/null @@ -1,52 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Tests QUIC server busy support - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { once } = require('events'); -const { key, cert, ca } = require('../common/quic'); - -const { createQuicSocket } = require('net'); -const options = { key, cert, ca, alpn: 'zzz' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -client.on('close', common.mustCall()); -server.on('close', common.mustCall()); -server.on('listening', common.mustCall()); -server.on('busy', common.mustCall((busy) => { - assert.strictEqual(busy, true); -})); - -// When the server is set as busy, all connections -// will be rejected with a SERVER_BUSY response. -server.serverBusy = true; - -(async function() { - server.on('session', common.mustNotCall()); - await server.listen(); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - - req.on('secure', common.mustNotCall()); - - req.on('close', common.mustCall(() => { - assert.strictEqual(req.closeCode.code, 2); - server.close(); - client.close(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsocket.js b/test/parallel/test-quic-quicsocket.js deleted file mode 100644 index e4ee4895064..00000000000 --- a/test/parallel/test-quic-quicsocket.js +++ /dev/null @@ -1,150 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Test QuicSocket constructor option errors. - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); - -const { createQuicSocket } = require('net'); - -const socket = createQuicSocket(); -assert(socket); - -// Before listen is called, serverSecureContext is always undefined. -assert.strictEqual(socket.serverSecureContext, undefined); - -assert.deepStrictEqual(socket.endpoints.length, 1); - -// Socket is not bound, so address should be empty -assert.deepStrictEqual(socket.endpoints[0].address, {}); - -// Socket is not bound -assert(!socket.bound); - -// Socket is not pending -assert(!socket.pending); - -// Socket is not destroyed -assert(!socket.destroyed); - -assert.strictEqual(typeof socket.duration, 'number'); -assert.strictEqual(typeof socket.boundDuration, 'number'); -assert.strictEqual(typeof socket.listenDuration, 'number'); -assert.strictEqual(typeof socket.bytesReceived, 'number'); -assert.strictEqual(socket.bytesReceived, 0); -assert.strictEqual(socket.bytesSent, 0); -assert.strictEqual(socket.packetsReceived, 0); -assert.strictEqual(socket.packetsSent, 0); -assert.strictEqual(socket.serverSessions, 0); -assert.strictEqual(socket.clientSessions, 0); - -const endpoint = socket.endpoints[0]; -assert(endpoint); - -// Will throw because the QuicSocket is not bound -{ - const err = { code: 'EBADF' }; - assert.throws(() => endpoint.setTTL(1), err); - assert.throws(() => endpoint.setMulticastTTL(1), err); - assert.throws(() => endpoint.setBroadcast(), err); - assert.throws(() => endpoint.setMulticastLoopback(), err); - assert.throws(() => endpoint.setMulticastInterface('0.0.0.0'), err); - // TODO(@jasnell): Verify behavior of add/drop membership then test - // assert.throws(() => endpoint.addMembership( - // '127.0.0.1', '127.0.0.1'), err); - // assert.throws(() => endpoint.dropMembership( - // '127.0.0.1', '127.0.0.1'), err); -} - -['test', null, {}, [], 1n, false].forEach((rx) => { - assert.throws(() => socket.setDiagnosticPacketLoss({ rx }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -['test', null, {}, [], 1n, false].forEach((tx) => { - assert.throws(() => socket.setDiagnosticPacketLoss({ tx }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -[ - { rx: -1 }, - { rx: 1.1 }, - { tx: -1 }, - { tx: 1.1 } -].forEach((options) => { - assert.throws(() => socket.setDiagnosticPacketLoss(options), { - code: 'ERR_OUT_OF_RANGE' - }); -}); - -[1, 1n, [], {}, null].forEach((arg) => { - assert.throws(() => socket.serverBusy = arg, { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - -(async function() { - const p = socket.listen({ alpn: 'zzz' }); - assert(socket.pending); - - await p; - - assert(endpoint.bound); - - // QuicSocket is already listening. - await assert.rejects(socket.listen(), { - code: 'ERR_INVALID_STATE' - }); - - assert.strictEqual(typeof endpoint.address.address, 'string'); - assert.strictEqual(typeof endpoint.address.port, 'number'); - assert.strictEqual(typeof endpoint.address.family, 'string'); - - if (!common.isWindows) - assert.strictEqual(typeof endpoint.fd, 'number'); - - endpoint.setTTL(1); - endpoint.setMulticastTTL(1); - endpoint.setBroadcast(); - endpoint.setBroadcast(true); - endpoint.setBroadcast(false); - - endpoint.setMulticastLoopback(); - endpoint.setMulticastLoopback(true); - endpoint.setMulticastLoopback(false); - - endpoint.setMulticastInterface('0.0.0.0'); - - socket.setDiagnosticPacketLoss({ rx: 0.5, tx: 0.5 }); - - socket.destroy(); - assert(socket.destroyed); -})().then(common.mustCall()); - -socket.on('close', common.mustCall(() => { - [ - 'ref', - 'unref', - 'setTTL', - 'setMulticastTTL', - 'setBroadcast', - 'setMulticastLoopback', - 'setMulticastInterface', - 'addMembership', - 'dropMembership' - ].forEach((op) => { - assert.throws(() => endpoint[op](), { - code: 'ERR_INVALID_STATE' - }); - }); - - assert.throws(() => { socket.serverBusy = true; }, { - code: 'ERR_INVALID_STATE' - }); -})); diff --git a/test/parallel/test-quic-quicstream-close-early.js b/test/parallel/test-quic-quicstream-close-early.js deleted file mode 100644 index b134b39e2f9..00000000000 --- a/test/parallel/test-quic-quicstream-close-early.js +++ /dev/null @@ -1,84 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { key, cert, ca } = require('../common/quic'); -const { once } = require('events'); -const { createQuicSocket } = require('net'); - -const qlog = process.env.NODE_QLOG === '1'; -const { createWriteStream } = require('fs'); - -const options = { key, cert, ca, alpn: 'zzz', qlog }; - -const client = createQuicSocket({ qlog, client: options }); -const server = createQuicSocket({ qlog, server: options }); - -const countdown = new Countdown(2, () => { - server.close(); - client.close(); -}); - -(async function() { - server.on('session', common.mustCall(async (session) => { - if (qlog) session.qlog.pipe(createWriteStream('server.qlog')); - const uni = await session.openStream({ halfOpen: true }); - uni.write('hi', common.mustSucceed()); - uni.on('error', common.mustNotCall()); - uni.on('data', common.mustNotCall()); - uni.on('close', common.mustCall()); - uni.close(); - - session.on('stream', common.mustCall((stream) => { - assert(stream.bidirectional); - assert(stream.readable); - assert(stream.writable); - stream.on('close', common.mustCall()); - stream.end(); - stream.resume(); - })); - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port, - }); - if (qlog) req.qlog.pipe(createWriteStream('client.qlog')); - - req.on('stream', common.mustCall((stream) => { - assert(stream.unidirectional); - assert(stream.readable); - assert(!stream.writable); - stream.on('data', common.mustCall((chunk) => { - assert.strictEqual(chunk.toString(), 'hi'); - })); - stream.on('end', common.mustCall()); - stream.on('close', common.mustCall(() => { - countdown.dec(); - })); - })); - - const stream = await req.openStream(); - stream.write('hello', common.mustSucceed()); - stream.write('there', common.mustSucceed()); - stream.resume(); - stream.on('error', common.mustNotCall()); - stream.on('end', common.mustCall()); - stream.on('close', common.mustCall()); - await stream.close(); - countdown.dec(); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicstream-destroy.js b/test/parallel/test-quic-quicstream-destroy.js deleted file mode 100644 index 3f0b0bf49e7..00000000000 --- a/test/parallel/test-quic-quicstream-destroy.js +++ /dev/null @@ -1,59 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Test that destroying a QuicStream immediately and synchronously -// after creation does not crash the process and closes the streams -// abruptly on both ends of the connection. - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { once } = require('events'); -const { key, cert, ca } = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -(async function() { - server.on('session', common.mustCall((session) => { - session.on('stream', common.mustCall((stream) => { - stream.destroy(); - stream.on('close', common.mustCall()); - stream.on('error', common.mustNotCall()); - assert(stream.destroyed); - })); - })); - - await server.listen(); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port - }); - - const stream = await req.openStream(); - stream.end('foo'); - // Do not explicitly end the stream here. - - stream.resume(); - stream.on('end', common.mustCall()); - - stream.on('close', common.mustCall(() => { - assert(stream.destroyed); - client.close(); - server.close(); - })); - - req.on('close', common.mustCall()); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicstream-identifiers.js b/test/parallel/test-quic-quicstream-identifiers.js deleted file mode 100644 index 98ffc3e9a05..00000000000 --- a/test/parallel/test-quic-quicstream-identifiers.js +++ /dev/null @@ -1,141 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Tests that both client and server can open -// bidirectional and unidirectional streams, -// and that the properties for each are set -// accordingly. -// -// +------+----------------------------------+ -// | ID | Stream Type | -// +------+----------------------------------+ -// | 0 | Client-Initiated, Bidirectional | -// | | | -// | 1 | Server-Initiated, Bidirectional | -// | | | -// | 2 | Client-Initiated, Unidirectional | -// | | | -// | 3 | Server-Initiated, Unidirectional | -// +------+----------------------------------+ - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const Countdown = require('../common/countdown'); -const assert = require('assert'); -const { once } = require('events'); -const { key, cert, ca } = require('../common/quic'); - -const { createQuicSocket } = require('net'); -const options = { key, cert, ca, alpn: 'zzz' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -const countdown = new Countdown(4, () => { - server.close(); - client.close(); -}); - -const closeHandler = common.mustCall(() => countdown.dec(), 4); - -(async function() { - server.on('session', common.mustCall(async (session) => { - ([3, 1n, [], {}, null, 'meow']).forEach((halfOpen) => { - assert.rejects( - session.openStream({ halfOpen }), { - code: 'ERR_INVALID_ARG_TYPE' - }); - }); - - const uni = await session.openStream({ halfOpen: true }); - uni.end('test'); - - const bidi = await session.openStream(); - bidi.end('test'); - bidi.resume(); - bidi.on('end', common.mustCall()); - - assert.strictEqual(uni.id, 3); - assert(uni.unidirectional); - assert(uni.serverInitiated); - assert(!uni.bidirectional); - assert(!uni.clientInitiated); - - assert.strictEqual(bidi.id, 1); - assert(bidi.bidirectional); - assert(bidi.serverInitiated); - assert(!bidi.unidirectional); - assert(!bidi.clientInitiated); - - session.on('stream', common.mustCall((stream) => { - assert(stream.clientInitiated); - assert(!stream.serverInitiated); - switch (stream.id) { - case 0: - assert(stream.bidirectional); - assert(!stream.unidirectional); - stream.end('test'); - break; - case 2: - assert(stream.unidirectional); - assert(!stream.bidirectional); - break; - } - stream.resume(); - stream.on('end', common.mustCall()); - }, 2)); - })); - - await server.listen(); - - const req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - - const bidi = await req.openStream(); - bidi.end('test'); - bidi.resume(); - bidi.on('close', closeHandler); - assert.strictEqual(bidi.id, 0); - - assert(bidi.clientInitiated); - assert(bidi.bidirectional); - assert(!bidi.serverInitiated); - assert(!bidi.unidirectional); - - const uni = await req.openStream({ halfOpen: true }); - uni.end('test'); - uni.on('close', closeHandler); - assert.strictEqual(uni.id, 2); - - assert(uni.clientInitiated); - assert(!uni.bidirectional); - assert(!uni.serverInitiated); - assert(uni.unidirectional); - - req.on('stream', common.mustCall((stream) => { - assert(stream.serverInitiated); - assert(!stream.clientInitiated); - switch (stream.id) { - case 1: - assert(!stream.unidirectional); - assert(stream.bidirectional); - stream.end(); - break; - case 3: - assert(stream.unidirectional); - assert(!stream.bidirectional); - } - stream.resume(); - stream.on('end', common.mustCall()); - stream.on('close', closeHandler); - }, 2)); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-server-busy-event-error-async.js b/test/parallel/test-quic-server-busy-event-error-async.js deleted file mode 100644 index 7e8f86b6898..00000000000 --- a/test/parallel/test-quic-server-busy-event-error-async.js +++ /dev/null @@ -1,32 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('busy', common.mustCall(async () => { - throw new Error('boom'); -})); - -server.on('close', common.mustCall()); - -server.on('error', common.mustCall((err) => { - assert.strictEqual(err.message, 'boom'); -})); - -assert.strictEqual(server.serverBusy, false); -server.serverBusy = true; diff --git a/test/parallel/test-quic-server-busy-event-error.js b/test/parallel/test-quic-server-busy-event-error.js deleted file mode 100644 index d71e9d25e70..00000000000 --- a/test/parallel/test-quic-server-busy-event-error.js +++ /dev/null @@ -1,32 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('busy', common.mustCall(() => { - throw new Error('boom'); -})); - -server.on('close', common.mustCall()); - -server.on('error', common.mustCall((err) => { - assert.strictEqual(err.message, 'boom'); -})); - -assert.strictEqual(server.serverBusy, false); -server.serverBusy = true; diff --git a/test/parallel/test-quic-server-listening-event-error-async.js b/test/parallel/test-quic-server-listening-event-error-async.js deleted file mode 100644 index f4c54407359..00000000000 --- a/test/parallel/test-quic-server-listening-event-error-async.js +++ /dev/null @@ -1,33 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('session', common.mustNotCall()); - -server.on('error', common.mustCall((error) => { - assert.strictEqual(error.message, 'boom'); -})); - -server.on('ready', common.mustCall()); - -server.on('listening', common.mustCall(async () => { - throw new Error('boom'); -})); - -server.listen(); diff --git a/test/parallel/test-quic-server-listening-event-error.js b/test/parallel/test-quic-server-listening-event-error.js deleted file mode 100644 index e91b4bb41cb..00000000000 --- a/test/parallel/test-quic-server-listening-event-error.js +++ /dev/null @@ -1,33 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('session', common.mustNotCall()); - -server.on('error', common.mustCall((error) => { - assert.strictEqual(error.message, 'boom'); -})); - -server.on('ready', common.mustCall()); - -server.on('listening', common.mustCall(() => { - throw new Error('boom'); -})); - -server.listen(); diff --git a/test/parallel/test-quic-server-ready-event-error-async.js b/test/parallel/test-quic-server-ready-event-error-async.js deleted file mode 100644 index 4d42d4725df..00000000000 --- a/test/parallel/test-quic-server-ready-event-error-async.js +++ /dev/null @@ -1,31 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('session', common.mustNotCall()); - -server.listen(); - -server.on('error', common.mustCall((error) => { - assert.strictEqual(error.message, 'boom'); -})); - -server.on('ready', common.mustCall(async () => { - throw new Error('boom'); -})); diff --git a/test/parallel/test-quic-server-ready-event-error.js b/test/parallel/test-quic-server-ready-event-error.js deleted file mode 100644 index df3e6265309..00000000000 --- a/test/parallel/test-quic-server-ready-event-error.js +++ /dev/null @@ -1,31 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('session', common.mustNotCall()); - -server.listen(); - -server.on('error', common.mustCall((error) => { - assert.strictEqual(error.message, 'boom'); -})); - -server.on('ready', common.mustCall(() => { - throw new Error('boom'); -})); diff --git a/test/parallel/test-quic-server-session-event-error-async.js b/test/parallel/test-quic-server-session-event-error-async.js deleted file mode 100644 index b5c039846c3..00000000000 --- a/test/parallel/test-quic-server-session-event-error-async.js +++ /dev/null @@ -1,65 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { once } = require('events'); -const { internalBinding } = require('internal/test/binding'); -const { - constants: { - NGTCP2_CONNECTION_REFUSED - } -} = internalBinding('quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -(async function() { - server.on('session', common.mustCall(async (session) => { - session.on('close', common.mustCall()); - session.on('error', common.mustCall((err) => { - assert.strictEqual(err.message, 'boom'); - })); - // Throwing inside the session event handler should cause - // the session to be destroyed immediately. This should - // cause the client side to be closed also. - throw new Error('boom'); - })); - - server.on('sessionError', common.mustCall((err, session) => { - assert.strictEqual(err.message, 'boom'); - assert(session.destroyed); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('close', common.mustCall(() => { - assert.strictEqual(req.closeCode.code, NGTCP2_CONNECTION_REFUSED); - assert.strictEqual(req.closeCode.silent, true); - server.close(); - client.close(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-server-session-event-error.js b/test/parallel/test-quic-server-session-event-error.js deleted file mode 100644 index 09c9046a791..00000000000 --- a/test/parallel/test-quic-server-session-event-error.js +++ /dev/null @@ -1,65 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { once } = require('events'); -const { internalBinding } = require('internal/test/binding'); -const { - constants: { - NGTCP2_CONNECTION_REFUSED - } -} = internalBinding('quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -(async function() { - server.on('session', common.mustCall((session) => { - session.on('close', common.mustCall()); - session.on('error', common.mustCall((err) => { - assert.strictEqual(err.message, 'boom'); - })); - // Throwing inside the session event handler should cause - // the session to be destroyed immediately. This should - // cause the client side to be closed also. - throw new Error('boom'); - })); - - server.on('sessionError', common.mustCall((err, session) => { - assert.strictEqual(err.message, 'boom'); - assert(session.destroyed); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('close', common.mustCall(() => { - assert.strictEqual(req.closeCode.code, NGTCP2_CONNECTION_REFUSED); - assert.strictEqual(req.closeCode.silent, true); - server.close(); - client.close(); - })); - - await Promise.all([ - once(server, 'close'), - once(client, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-simple-client-migrate.js b/test/parallel/test-quic-simple-client-migrate.js deleted file mode 100644 index 2aed15704a1..00000000000 --- a/test/parallel/test-quic-simple-client-migrate.js +++ /dev/null @@ -1,96 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { key, cert, ca } = require('../common/quic'); - -const { once } = require('events'); -const { createQuicSocket } = require('net'); -const { pipeline } = require('stream'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -let req; -const client = createQuicSocket({ client: options }); -const client2 = createQuicSocket({ client: options }); -const server = createQuicSocket({ server: options }); - -(async function() { - server.on('session', common.mustCall((session) => { - session.on('stream', common.mustCall(async (stream) => { - pipeline(stream, stream, common.mustSucceed()); - (await session.openStream({ halfOpen: true })) - .end('Hello from the server'); - })); - })); - - await server.listen(); - - req = await client.connect({ - address: common.localhostIPv4, - port: server.endpoints[0].address.port, - }); - - req.on('close', () => { - client2.close(); - server.close(); - }); - - req.on('stream', common.mustCall((stream) => { - let data = ''; - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, 'Hello from the server'); - })); - stream.on('close', common.mustCall()); - })); - - let data = ''; - const stream = await req.openStream(); - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', common.mustCall(() => { - assert.strictEqual(data, 'Hello from the client'); - })); - stream.on('close', common.mustCall(() => { - req.close(); - })); - // Send some data on one connection... - stream.write('Hello '); - - // Wait just a bit, then migrate to a different - // QuicSocket and continue sending. - setTimeout(common.mustCall(async () => { - const s1 = req.socket; - const a1 = req.socket.endpoints[0].address; - - await Promise.all([1, {}, 'test', false, null, undefined].map((i) => { - return assert.rejects(req.setSocket(i), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - await Promise.all([1, {}, 'test', null].map((i) => { - return assert.rejects(req.setSocket(req.socket, i), { - code: 'ERR_INVALID_ARG_TYPE' - }); - })); - - await req.setSocket(client2); - - // Verify that it is using a different network endpoint - assert.notStrictEqual(s1, req.socket); - assert.notDeepStrictEqual(a1, req.socket.endpoints[0].address); - client.close(); - stream.end('from the client'); - }), common.platformTimeout(100)); - - await Promise.all([ - once(server, 'close'), - once(client2, 'close') - ]); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-simple-server-bidi.js b/test/parallel/test-quic-simple-server-bidi.js deleted file mode 100644 index 0b12a9d5452..00000000000 --- a/test/parallel/test-quic-simple-server-bidi.js +++ /dev/null @@ -1,55 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { key, cert, ca } = require('../common/quic'); - -const { createWriteStream } = require('fs'); -const { createQuicSocket } = require('net'); -const { strictEqual } = require('assert'); - -const qlog = process.env.NODE_QLOG === '1'; - -const options = { key, cert, ca, alpn: 'zzz', qlog }; - -const client = createQuicSocket({ qlog, client: options }); -const server = createQuicSocket({ qlog, server: options }); - -(async function() { - server.on('session', common.mustCall(async (session) => { - if (qlog) session.qlog.pipe(createWriteStream('server.qlog')); - const stream = await session.openStream(); - stream.resume(); - stream.write('from the '); - setTimeout(() => stream.end('server'), common.platformTimeout(10)); - - session.on('close', common.mustCall(() => { - server.close(); - })); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - if (qlog) req.qlog.pipe(createWriteStream('client.qlog')); - - req.on('stream', common.mustCall(async (stream) => { - let data = ''; - stream.setEncoding('utf8'); - - stream.end('foo'); - - for await (const chunk of stream) - data += chunk; - strictEqual(data, 'from the server'); - - await req.close(); - client.close(); - })); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-simple-server-uni.js b/test/parallel/test-quic-simple-server-uni.js deleted file mode 100644 index 19498f0f34c..00000000000 --- a/test/parallel/test-quic-simple-server-uni.js +++ /dev/null @@ -1,60 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { key, cert, ca } = require('../common/quic'); - -const { createWriteStream } = require('fs'); -const { createQuicSocket } = require('net'); -const { strictEqual } = require('assert'); - -const qlog = process.env.NODE_QLOG === '1'; - -const options = { key, cert, ca, alpn: 'zzz', qlog }; - -const client = createQuicSocket({ qlog, client: options }); -const server = createQuicSocket({ qlog, server: options }); - -server.on('close', common.mustCall()); -client.on('close', common.mustCall()); - -(async function() { - server.on('session', common.mustCall(async (session) => { - if (qlog) session.qlog.pipe(createWriteStream('server.qlog')); - const stream = await session.openStream({ halfOpen: true }); - stream.write('from the '); - setTimeout(() => stream.end('server'), common.platformTimeout(10)); - stream.on('close', common.mustCall()); - session.on('close', common.mustCall(() => { - server.close(); - })); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - if (qlog) req.qlog.pipe(createWriteStream('client.qlog')); - - req.on('close', common.mustCall()); - - req.on('stream', common.mustCall(async (stream) => { - let data = ''; - stream.setEncoding('utf8'); - stream.on('close', common.mustCall()); - - for await (const chunk of stream) - data += chunk; - - strictEqual(data, 'from the server'); - - await req.close(); - - client.close(); - })); -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-socket-close-event-error-async.js b/test/parallel/test-quic-socket-close-event-error-async.js deleted file mode 100644 index 3b4baf595a6..00000000000 --- a/test/parallel/test-quic-socket-close-event-error-async.js +++ /dev/null @@ -1,31 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('error', common.mustNotCall()); - -server.on('close', common.mustCall(async () => { - throw new Error('boom'); -})); - -process.on('uncaughtException', (error) => { - assert.strictEqual(error.message, 'boom'); -}); - -server.destroy(); diff --git a/test/parallel/test-quic-socket-close-event-error.js b/test/parallel/test-quic-socket-close-event-error.js deleted file mode 100644 index 5160698518f..00000000000 --- a/test/parallel/test-quic-socket-close-event-error.js +++ /dev/null @@ -1,31 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const assert = require('assert'); -const { - key, - cert, - ca, -} = require('../common/quic'); - -const { createQuicSocket } = require('net'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const server = createQuicSocket({ server: options }); - -server.on('error', common.mustNotCall()); - -server.on('close', common.mustCall(() => { - throw new Error('boom'); -})); - -process.on('uncaughtException', (error) => { - assert.strictEqual(error.message, 'boom'); -}); - -server.destroy(); diff --git a/test/parallel/test-quic-statelessreset.js b/test/parallel/test-quic-statelessreset.js deleted file mode 100644 index 70fc8c0fd02..00000000000 --- a/test/parallel/test-quic-statelessreset.js +++ /dev/null @@ -1,71 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; - -// Testing stateless reset - -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { internalBinding } = require('internal/test/binding'); -const assert = require('assert'); - -const { key, cert, ca } = require('../common/quic'); - -const { - kHandle, -} = require('internal/stream_base_commons'); -const { silentCloseSession } = internalBinding('quic'); - -const { createQuicSocket } = require('net'); - -const kStatelessResetToken = - Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'); - -const options = { key, cert, ca, alpn: 'zzz' }; - -const client = createQuicSocket({ client: options }); -const server = createQuicSocket({ - statelessResetSecret: kStatelessResetToken, - server: options -}); - -server.on('close', common.mustCall(() => { - // Verify stats recording - assert(server.statelessResetCount >= 1); -})); - -(async function() { - server.on('session', common.mustCall((session) => { - session.on('stream', common.mustCall((stream) => { - // silentCloseSession is an internal-only testing tool - // that allows us to prematurely destroy a QuicSession - // without the proper communication flow with the connected - // peer. We call this to simulate a local crash that loses - // state, which should trigger the server to send a - // stateless reset token to the client. - silentCloseSession(session[kHandle]); - })); - - session.on('close', common.mustCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port, - }); - - req.on('close', common.mustCall(() => { - assert.strictEqual(req.statelessReset, true); - server.close(); - client.close(); - })); - - const stream = await req.openStream(); - stream.end('hello'); - stream.resume(); - stream.on('close', common.mustCall()); - -})().then(common.mustCall()); diff --git a/test/parallel/test-quic-with-fake-udp.js b/test/parallel/test-quic-with-fake-udp.js deleted file mode 100644 index 3baf4ec4a69..00000000000 --- a/test/parallel/test-quic-with-fake-udp.js +++ /dev/null @@ -1,60 +0,0 @@ -// Flags: --expose-internals --no-warnings -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -// Tests that QUIC works properly when using a pair of mocked UDP ports. - -const { makeUDPPair } = require('../common/udppair'); -const assert = require('assert'); -const { createQuicSocket } = require('net'); -const { kUDPHandleForTesting } = require('internal/quic/core'); - -const { key, cert, ca } = require('../common/quic'); - -const options = { key, cert, ca, alpn: 'meow' }; - -const { serverSide, clientSide } = makeUDPPair(); - -const server = createQuicSocket({ - endpoint: { [kUDPHandleForTesting]: serverSide._handle }, - server: options -}); -serverSide.afterBind(); - -const client = createQuicSocket({ - endpoint: { [kUDPHandleForTesting]: clientSide._handle }, - client: options -}); -clientSide.afterBind(); - -(async function() { - server.on('session', common.mustCall(async (session) => { - session.on('close', common.mustNotCall()); - const stream = await session.openStream({ halfOpen: false }); - stream.end('Hi!'); - stream.on('data', common.mustNotCall()); - stream.on('finish', common.mustCall()); - stream.on('close', common.mustNotCall()); - stream.on('end', common.mustNotCall()); - })); - - await server.listen(); - - const req = await client.connect({ - address: 'localhost', - port: server.endpoints[0].address.port - }); - - req.on('stream', common.mustCall((stream) => { - stream.on('data', common.mustCall((data) => { - assert.strictEqual(data.toString(), 'Hi!'); - })); - - stream.on('end', common.mustCall()); - })); - - req.on('close', common.mustNotCall()); - -})().then(common.mustCall()); diff --git a/test/pummel/test-heapdump-quic.js b/test/pummel/test-heapdump-quic.js deleted file mode 100644 index a3e5f95166a..00000000000 --- a/test/pummel/test-heapdump-quic.js +++ /dev/null @@ -1,152 +0,0 @@ -// Flags: --expose-internals -'use strict'; -const common = require('../common'); -if (!common.hasQuic) - common.skip('missing quic'); - -const { createQuicSocket } = require('net'); - -const { recordState } = require('../common/heap'); -const fixtures = require('../common/fixtures'); -const key = fixtures.readKey('agent1-key.pem', 'binary'); -const cert = fixtures.readKey('agent1-cert.pem', 'binary'); -const ca = fixtures.readKey('ca1-cert.pem', 'binary'); - -{ - const state = recordState(); - state.validateSnapshotNodes('Node / QuicStream', []); - state.validateSnapshotNodes('Node / QuicSession', []); - state.validateSnapshotNodes('Node / QuicSocket', []); -} - -const server = createQuicSocket({ port: 0, validateAddress: true }); - -server.listen({ - key, - cert, - ca, - rejectUnauthorized: false, - maxCryptoBuffer: 4096, - alpn: 'meow' -}); - -server.on('session', common.mustCall((session) => { - session.on('secure', common.mustCall((servername, alpn, cipher) => { - // eslint-disable-next-line no-unused-vars - const stream = session.openStream({ halfOpen: false }); - - const state = recordState(); - - state.validateSnapshotNodes('Node / QuicSocket', [ - { - children: [ - { node_name: 'QuicSocket', edge_name: 'wrapped' }, - { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, - { node_name: 'Node / sessions', edge_name: 'sessions' }, - { node_name: 'Node / dcid_to_scid', edge_name: 'dcid_to_scid' }, - ] - } - ], { loose: true }); - - state.validateSnapshotNodes('Node / QuicStream', [ - { - children: [ - { node_name: 'QuicStream', edge_name: 'wrapped' }, - { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, - { node_name: 'Node / QuicBuffer', edge_name: 'buffer' }, - { node_name: 'Node / HistogramBase', edge_name: 'data_rx_rate' }, - { node_name: 'Node / HistogramBase', edge_name: 'data_rx_size' }, - { node_name: 'Node / HistogramBase', edge_name: 'data_rx_ack' } - ] - } - ], { loose: true }); - - state.validateSnapshotNodes('Node / QuicBuffer', [ - { - children: [ - { node_name: 'Node / length', edge_name: 'length' } - ] - } - ], { loose: true }); - - state.validateSnapshotNodes('Node / QuicSession', [ - { - children: [ - { node_name: 'QuicServerSession', edge_name: 'wrapped' }, - { node_name: 'Node / QuicCryptoContext', - edge_name: 'crypto_context' }, - { node_name: 'Node / HistogramBase', edge_name: 'crypto_rx_ack' }, - { node_name: 'Node / HistogramBase', - edge_name: 'crypto_handshake_rate' }, - { node_name: 'Node / Timer', edge_name: 'retransmit' }, - { node_name: 'Node / Timer', edge_name: 'idle' }, - { node_name: 'Node / QuicBuffer', edge_name: 'sendbuf' }, - { node_name: 'Node / QuicBuffer', edge_name: 'txbuf' }, - { node_name: 'Float64Array', edge_name: 'recovery_stats_buffer' }, - { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, - { node_name: 'Node / current_ngtcp2_memory', - edge_name: 'current_ngtcp2_memory' }, - { node_name: 'Node / streams', edge_name: 'streams' }, - { node_name: 'Node / std::basic_string', edge_name: 'alpn' }, - { node_name: 'Node / std::basic_string', edge_name: 'hostname' }, - { node_name: 'Float64Array', edge_name: 'state' }, - ] - }, - { - children: [ - { node_name: 'QuicClientSession', edge_name: 'wrapped' }, - { node_name: 'Node / QuicCryptoContext', - edge_name: 'crypto_context' }, - { node_name: 'Node / HistogramBase', edge_name: 'crypto_rx_ack' }, - { node_name: 'Node / HistogramBase', - edge_name: 'crypto_handshake_rate' }, - { node_name: 'Node / Timer', edge_name: 'retransmit' }, - { node_name: 'Node / Timer', edge_name: 'idle' }, - { node_name: 'Node / QuicBuffer', edge_name: 'sendbuf' }, - { node_name: 'Node / QuicBuffer', edge_name: 'txbuf' }, - { node_name: 'Float64Array', edge_name: 'recovery_stats_buffer' }, - { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, - { node_name: 'Node / current_ngtcp2_memory', - edge_name: 'current_ngtcp2_memory' }, - { node_name: 'Node / streams', edge_name: 'streams' }, - { node_name: 'Node / std::basic_string', edge_name: 'alpn' }, - { node_name: 'Node / std::basic_string', edge_name: 'hostname' }, - { node_name: 'Float64Array', edge_name: 'state' }, - ] - } - ], { loose: true }); - - state.validateSnapshotNodes('Node / QuicCryptoContext', [ - { - children: [ - { node_name: 'Node / rx_secret', edge_name: 'rx_secret' }, - { node_name: 'Node / tx_secret', edge_name: 'tx_secret' }, - { node_name: 'Node / QuicBuffer', edge_name: 'initial_crypto' }, - { node_name: 'Node / QuicBuffer', - edge_name: 'handshake_crypto' }, - { node_name: 'Node / QuicBuffer', edge_name: 'app_crypto' }, - ] - } - ], { loose: true }); - - session.destroy(); - server.close(); - })); -})); - -server.on('ready', common.mustCall(() => { - const client = createQuicSocket({ - port: 0, - client: { - key, - cert, - ca, - alpn: 'meow' - } - }); - - client.connect({ - address: 'localhost', - port: server.address.port - }).on('close', common.mustCall(() => client.close())); -})); diff --git a/test/sequential/test-async-wrap-getasyncid.js b/test/sequential/test-async-wrap-getasyncid.js index f89b5463447..e6464466aa4 100644 --- a/test/sequential/test-async-wrap-getasyncid.js +++ b/test/sequential/test-async-wrap-getasyncid.js @@ -46,12 +46,6 @@ const { getSystemErrorName } = require('util'); delete providers.MESSAGEPORT; delete providers.WORKER; // TODO(danbev): Test for these - delete providers.QUICCLIENTSESSION; - delete providers.QUICSERVERSESSION; - delete providers.QUICSENDWRAP; - delete providers.QUICSOCKET; - delete providers.QUICSTREAM; - delete providers.QLOGSTREAM; delete providers.JSUDPWRAP; if (!common.isMainThread) delete providers.INSPECTORJSBINDING; diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 41e6086bf00..70a3fede3d5 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -189,10 +189,6 @@ const customTypesMap = { 'perf_hooks.html#perf_hooks_class_perf_hooks_performanceobserver', 'PerformanceObserverEntryList': 'perf_hooks.html#perf_hooks_class_performanceobserverentrylist', - 'QuicEndpoint': 'quic.html#quic_class_quicendpoint', - 'QuicSession': 'quic.html#quic_class_quicserversession_extends_quicsession', - 'QuicSocket': 'quic.html#quic_net_createquicsocket_options', - 'QuicStream': 'quic.html#quic_class_quicstream_extends_stream_duplex', 'readline.Interface': 'readline.html#readline_class_interface', diff --git a/tools/license-builder.sh b/tools/license-builder.sh index 0f5fd6a155f..d1d173943c1 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -89,12 +89,6 @@ addlicense "gtest" "test/cctest/gtest" "$(cat "${rootdir}"/test/cctest/gtest/LIC # nghttp2 addlicense "nghttp2" "deps/nghttp2" "$(cat "${rootdir}"/deps/nghttp2/COPYING)" -# ngtcp2 -addlicense "ngtcp2" "deps/ngtcp2" "$(cat "${rootdir}"/deps/ngtcp2/COPYING)" - -# nghttp3 -addlicense "nghttp3" "deps/nghttp3" "$(cat "${rootdir}"/deps/nghttp3/COPYING)" - # node-inspect addlicense "node-inspect" "deps/node-inspect" "$(cat "${rootdir}"/deps/node-inspect/LICENSE)" diff --git a/vcbuild.bat b/vcbuild.bat index 777842de209..369e8574bef 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -70,7 +70,6 @@ set openssl_no_asm= set doc= set extra_msbuild_args= set exit_code=0 -set experimental_quic= :next-arg if "%1"=="" goto args-done @@ -145,7 +144,6 @@ if /i "%1"=="cctest" set cctest=1&goto arg-ok if /i "%1"=="openssl-no-asm" set openssl_no_asm=1&goto arg-ok if /i "%1"=="doc" set doc=1&goto arg-ok if /i "%1"=="binlog" set extra_msbuild_args=/binaryLogger:%config%\node.binlog&goto arg-ok -if /i "%1"=="experimental-quic" set experimental_quic=1&goto arg-ok echo Error: invalid command line option `%1`. exit /b 1 @@ -197,7 +195,6 @@ if defined config_flags set configure_flags=%configure_flags% %config_flags% if defined target_arch set configure_flags=%configure_flags% --dest-cpu=%target_arch% if defined openssl_no_asm set configure_flags=%configure_flags% --openssl-no-asm if defined DEBUG_HELPER set configure_flags=%configure_flags% --verbose -if defined experimental_quic set configure_flags=%configure_flags% --experimental-quic if "%target_arch%"=="x86" if "%PROCESSOR_ARCHITECTURE%"=="AMD64" set configure_flags=%configure_flags% --no-cross-compiling if "%target_arch%"=="arm64" set configure_flags=%configure_flags% --cross-compiling @@ -688,7 +685,7 @@ set exit_code=1 goto exit :help -echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-benchmark/test-internet/test-pummel/test-simple/test-message/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-async-hooks/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [ignore-flaky] [static/dll] [noprojgen] [projgen] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [noetw] [ltcg] [licensetf] [sign] [ia32/x86/x64/arm64] [vs2019] [download-all] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] [experimental-quic] +echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-benchmark/test-internet/test-pummel/test-simple/test-message/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-async-hooks/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [ignore-flaky] [static/dll] [noprojgen] [projgen] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [noetw] [ltcg] [licensetf] [sign] [ia32/x86/x64/arm64] [vs2019] [download-all] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build |